From 367fd5af71fb26acee63b6ec823fb2c9a4c763b7 Mon Sep 17 00:00:00 2001 From: HorrorTroll Date: Sun, 18 Nov 2018 16:51:39 +0700 Subject: [PATCH 1/2] master --- ChocolArm64/ABitUtils.cs | 61 - ChocolArm64/AOpCodeTable.cs | 684 --- ChocolArm64/AOptimizations.cs | 18 - ChocolArm64/AThread.cs | 67 - ChocolArm64/ATranslatedSub.cs | 150 - ChocolArm64/ATranslator.cs | 206 - ChocolArm64/BitUtils.cs | 49 + ChocolArm64/CpuThread.cs | 69 + ChocolArm64/Decoder/ABlock.cs | 35 - ChocolArm64/Decoder/ACond.cs | 22 - ChocolArm64/Decoder/ADecoder.cs | 243 - ChocolArm64/Decoder/ADecoderHelper.cs | 107 - ChocolArm64/Decoder/AOpCode.cs | 40 - ChocolArm64/Decoder/AOpCodeAdr.cs | 18 - ChocolArm64/Decoder/AOpCodeAlu.cs | 24 - ChocolArm64/Decoder/AOpCodeAluImm.cs | 39 - ChocolArm64/Decoder/AOpCodeAluRs.cs | 29 - ChocolArm64/Decoder/AOpCodeAluRx.cs | 19 - ChocolArm64/Decoder/AOpCodeBImm.cs | 11 - ChocolArm64/Decoder/AOpCodeBImmAl.cs | 12 - ChocolArm64/Decoder/AOpCodeBImmCmp.cs | 21 - ChocolArm64/Decoder/AOpCodeBImmCond.cs | 25 - ChocolArm64/Decoder/AOpCodeBImmTest.cs | 20 - ChocolArm64/Decoder/AOpCodeBReg.cs | 24 - ChocolArm64/Decoder/AOpCodeBfm.cs | 29 - ChocolArm64/Decoder/AOpCodeCcmp.cs | 31 - ChocolArm64/Decoder/AOpCodeCcmpImm.cs | 11 - ChocolArm64/Decoder/AOpCodeCcmpReg.cs | 15 - ChocolArm64/Decoder/AOpCodeCsel.cs | 17 - ChocolArm64/Decoder/AOpCodeException.cs | 14 - ChocolArm64/Decoder/AOpCodeMem.cs | 19 - ChocolArm64/Decoder/AOpCodeMemEx.cs | 16 - ChocolArm64/Decoder/AOpCodeMemPair.cs | 25 - ChocolArm64/Decoder/AOpCodeMemReg.cs | 20 - ChocolArm64/Decoder/AOpCodeMov.cs | 36 - ChocolArm64/Decoder/AOpCodeMul.cs | 16 - ChocolArm64/Decoder/AOpCodeSimd.cs | 25 - ChocolArm64/Decoder/AOpCodeSimdCvt.cs | 31 - ChocolArm64/Decoder/AOpCodeSimdExt.cs | 14 - ChocolArm64/Decoder/AOpCodeSimdFcond.cs | 17 - ChocolArm64/Decoder/AOpCodeSimdFmov.cs | 33 - ChocolArm64/Decoder/AOpCodeSimdImm.cs | 101 - ChocolArm64/Decoder/AOpCodeSimdIns.cs | 36 - ChocolArm64/Decoder/AOpCodeSimdMemImm.cs | 19 - ChocolArm64/Decoder/AOpCodeSimdMemLit.cs | 31 - ChocolArm64/Decoder/AOpCodeSimdMemPair.cs | 16 - ChocolArm64/Decoder/AOpCodeSimdMemReg.cs | 14 - ChocolArm64/Decoder/AOpCodeSimdMemSs.cs | 98 - ChocolArm64/Decoder/AOpCodeSimdReg.cs | 18 - ChocolArm64/Decoder/AOpCodeSimdRegElem.cs | 31 - ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs | 22 - ChocolArm64/Decoder/AOpCodeSimdShImm.cs | 16 - ChocolArm64/Decoder/AOpCodeSimdTbl.cs | 12 - ChocolArm64/Decoder/AOpCodeSystem.cs | 24 - ChocolArm64/Decoder/IAOpCode.cs | 13 - ChocolArm64/Decoder/IAOpCodeAlu.cs | 10 - ChocolArm64/Decoder/IAOpCodeAluImm.cs | 7 - ChocolArm64/Decoder/IAOpCodeAluRs.cs | 10 - ChocolArm64/Decoder/IAOpCodeAluRx.cs | 10 - ChocolArm64/Decoder/IAOpCodeCond.cs | 7 - ChocolArm64/Decoder/IAOpCodeSimd.cs | 7 - ChocolArm64/Decoder32/A32OpCode.cs | 15 - ChocolArm64/Decoder32/A32OpCodeBImmAl.cs | 16 - ChocolArm64/Decoders/Block.cs | 35 + ChocolArm64/Decoders/Cond.cs | 22 + .../ADataOp.cs => Decoders/DataOp.cs} | 4 +- ChocolArm64/Decoders/Decoder.cs | 239 + ChocolArm64/Decoders/DecoderHelper.cs | 107 + ChocolArm64/Decoders/IOpCode64.cs | 13 + ChocolArm64/Decoders/IOpCodeAlu64.cs | 10 + ChocolArm64/Decoders/IOpCodeAluImm64.cs | 7 + ChocolArm64/Decoders/IOpCodeAluRs64.cs | 10 + ChocolArm64/Decoders/IOpCodeAluRx64.cs | 10 + ChocolArm64/Decoders/IOpCodeCond64.cs | 7 + .../IOpCodeLit64.cs} | 4 +- ChocolArm64/Decoders/IOpCodeSimd64.cs | 7 + .../AIntType.cs => Decoders/IntType.cs} | 4 +- ChocolArm64/Decoders/OpCode64.cs | 40 + ChocolArm64/Decoders/OpCodeAdr64.cs | 18 + ChocolArm64/Decoders/OpCodeAlu64.cs | 24 + ChocolArm64/Decoders/OpCodeAluImm64.cs | 39 + ChocolArm64/Decoders/OpCodeAluRs64.cs | 29 + ChocolArm64/Decoders/OpCodeAluRx64.cs | 19 + ChocolArm64/Decoders/OpCodeBImm64.cs | 11 + ChocolArm64/Decoders/OpCodeBImmAl64.cs | 12 + ChocolArm64/Decoders/OpCodeBImmCmp64.cs | 21 + ChocolArm64/Decoders/OpCodeBImmCond64.cs | 25 + ChocolArm64/Decoders/OpCodeBImmTest64.cs | 20 + ChocolArm64/Decoders/OpCodeBReg64.cs | 24 + ChocolArm64/Decoders/OpCodeBfm64.cs | 29 + ChocolArm64/Decoders/OpCodeCcmp64.cs | 31 + ChocolArm64/Decoders/OpCodeCcmpImm64.cs | 11 + ChocolArm64/Decoders/OpCodeCcmpReg64.cs | 15 + ChocolArm64/Decoders/OpCodeCsel64.cs | 17 + ChocolArm64/Decoders/OpCodeException64.cs | 14 + ChocolArm64/Decoders/OpCodeMem64.cs | 19 + ChocolArm64/Decoders/OpCodeMemEx64.cs | 16 + .../OpCodeMemImm64.cs} | 24 +- .../OpCodeMemLit64.cs} | 14 +- ChocolArm64/Decoders/OpCodeMemPair64.cs | 25 + ChocolArm64/Decoders/OpCodeMemReg64.cs | 20 + ChocolArm64/Decoders/OpCodeMov64.cs | 36 + ChocolArm64/Decoders/OpCodeMul64.cs | 16 + ChocolArm64/Decoders/OpCodeSimd64.cs | 25 + ChocolArm64/Decoders/OpCodeSimdCvt64.cs | 31 + ChocolArm64/Decoders/OpCodeSimdExt64.cs | 14 + ChocolArm64/Decoders/OpCodeSimdFcond64.cs | 17 + ChocolArm64/Decoders/OpCodeSimdFmov64.cs | 33 + ChocolArm64/Decoders/OpCodeSimdImm64.cs | 101 + ChocolArm64/Decoders/OpCodeSimdIns64.cs | 36 + ChocolArm64/Decoders/OpCodeSimdMemImm64.cs | 19 + ChocolArm64/Decoders/OpCodeSimdMemLit64.cs | 31 + .../OpCodeSimdMemMs64.cs} | 28 +- ChocolArm64/Decoders/OpCodeSimdMemPair64.cs | 16 + ChocolArm64/Decoders/OpCodeSimdMemReg64.cs | 14 + ChocolArm64/Decoders/OpCodeSimdMemSs64.cs | 98 + ChocolArm64/Decoders/OpCodeSimdReg64.cs | 18 + ChocolArm64/Decoders/OpCodeSimdRegElem64.cs | 31 + ChocolArm64/Decoders/OpCodeSimdRegElemF64.cs | 33 + ChocolArm64/Decoders/OpCodeSimdShImm64.cs | 16 + ChocolArm64/Decoders/OpCodeSimdTbl64.cs | 12 + ChocolArm64/Decoders/OpCodeSystem64.cs | 24 + .../AShiftType.cs => Decoders/ShiftType.cs} | 4 +- ChocolArm64/Decoders32/A32OpCode.cs | 15 + ChocolArm64/Decoders32/A32OpCodeBImmAl.cs | 16 + ChocolArm64/Events/ACpuTraceEventArgs.cs | 17 - ChocolArm64/Events/AInstExceptionEventArgs.cs | 16 - ChocolArm64/Events/AInstUndefinedEventArgs.cs | 16 - ChocolArm64/Events/CpuTraceEventArgs.cs | 14 + ChocolArm64/Events/InstExceptionEventArgs.cs | 16 + ChocolArm64/Events/InstUndefinedEventArgs.cs | 16 + ChocolArm64/Events/InvalidAccessEventArgs.cs | 14 + .../Exceptions/VmmOutOfMemoryException.cs | 2 +- .../Exceptions/VmmPageFaultException.cs | 2 +- ChocolArm64/Instruction/AInst.cs | 20 - ChocolArm64/Instruction/AInstEmitAlu.cs | 392 -- ChocolArm64/Instruction/AInstEmitAluHelper.cs | 212 - ChocolArm64/Instruction/AInstEmitBfm.cs | 208 - ChocolArm64/Instruction/AInstEmitCcmp.cs | 81 - ChocolArm64/Instruction/AInstEmitCsel.cs | 58 - ChocolArm64/Instruction/AInstEmitException.cs | 86 - ChocolArm64/Instruction/AInstEmitFlow.cs | 220 - ChocolArm64/Instruction/AInstEmitHash.cs | 115 - ChocolArm64/Instruction/AInstEmitMemory.cs | 252 - ChocolArm64/Instruction/AInstEmitMemoryEx.cs | 189 - .../Instruction/AInstEmitMemoryHelper.cs | 138 - ChocolArm64/Instruction/AInstEmitMove.cs | 41 - ChocolArm64/Instruction/AInstEmitMul.cs | 80 - .../Instruction/AInstEmitSimdArithmetic.cs | 1500 ------ ChocolArm64/Instruction/AInstEmitSimdCmp.cs | 525 -- .../Instruction/AInstEmitSimdCrypto.cs | 54 - ChocolArm64/Instruction/AInstEmitSimdCvt.cs | 640 --- ChocolArm64/Instruction/AInstEmitSimdHash.cs | 61 - .../Instruction/AInstEmitSimdHelper.cs | 1400 ------ .../Instruction/AInstEmitSimdLogical.cs | 221 - .../Instruction/AInstEmitSimdMemory.cs | 185 - ChocolArm64/Instruction/AInstEmitSimdMove.cs | 422 -- ChocolArm64/Instruction/AInstEmitSimdShift.cs | 458 -- ChocolArm64/Instruction/AInstEmitSystem.cs | 133 - ChocolArm64/Instruction/AInstEmitter.cs | 6 - ChocolArm64/Instruction/AInstInterpreter.cs | 8 - ChocolArm64/Instruction/ASoftFallback.cs | 782 --- ChocolArm64/Instruction/ASoftFloat.cs | 537 -- ChocolArm64/Instruction/AVectorHelper.cs | 584 --- .../Instruction32/A32InstInterpretFlow.cs | 70 - .../Instruction32/A32InstInterpretHelper.cs | 65 - .../CryptoHelper.cs} | 166 +- ChocolArm64/Instructions/Inst.cs | 20 + ChocolArm64/Instructions/InstEmitAlu.cs | 402 ++ ChocolArm64/Instructions/InstEmitAluHelper.cs | 221 + ChocolArm64/Instructions/InstEmitBfm.cs | 208 + ChocolArm64/Instructions/InstEmitCcmp.cs | 81 + ChocolArm64/Instructions/InstEmitCsel.cs | 58 + ChocolArm64/Instructions/InstEmitException.cs | 86 + ChocolArm64/Instructions/InstEmitFlow.cs | 192 + ChocolArm64/Instructions/InstEmitHash.cs | 115 + ChocolArm64/Instructions/InstEmitMemory.cs | 252 + ChocolArm64/Instructions/InstEmitMemoryEx.cs | 192 + .../Instructions/InstEmitMemoryHelper.cs | 138 + ChocolArm64/Instructions/InstEmitMove.cs | 41 + ChocolArm64/Instructions/InstEmitMul.cs | 80 + .../Instructions/InstEmitSimdArithmetic.cs | 3013 ++++++++++++ ChocolArm64/Instructions/InstEmitSimdCmp.cs | 615 +++ .../Instructions/InstEmitSimdCrypto.cs | 54 + ChocolArm64/Instructions/InstEmitSimdCvt.cs | 757 +++ ChocolArm64/Instructions/InstEmitSimdHash.cs | 140 + .../Instructions/InstEmitSimdHelper.cs | 1513 ++++++ .../Instructions/InstEmitSimdLogical.cs | 311 ++ .../Instructions/InstEmitSimdMemory.cs | 185 + ChocolArm64/Instructions/InstEmitSimdMove.cs | 611 +++ ChocolArm64/Instructions/InstEmitSimdShift.cs | 865 ++++ ChocolArm64/Instructions/InstEmitSystem.cs | 138 + ChocolArm64/Instructions/InstEmitter.cs | 6 + ChocolArm64/Instructions/InstInterpreter.cs | 8 + ChocolArm64/Instructions/SoftFallback.cs | 922 ++++ ChocolArm64/Instructions/SoftFloat.cs | 2405 +++++++++ ChocolArm64/Instructions/VectorHelper.cs | 767 +++ .../A32InstInterpretAlu.cs | 2 +- .../Instructions32/A32InstInterpretFlow.cs | 70 + .../Instructions32/A32InstInterpretHelper.cs | 65 + ChocolArm64/Memory/AMemory.cs | 709 --- ChocolArm64/Memory/AMemoryHelper.cs | 67 - ChocolArm64/Memory/IAMemory.cs | 37 - ChocolArm64/Memory/IMemory.cs | 37 + ChocolArm64/Memory/MemoryHelper.cs | 67 + ChocolArm64/Memory/MemoryManager.cs | 767 +++ ChocolArm64/OpCodeTable.cs | 713 +++ ChocolArm64/Optimizations.cs | 18 + ChocolArm64/State/ARegister.cs | 142 - ChocolArm64/State/AThreadState.cs | 142 - ChocolArm64/State/CpuThreadState.cs | 164 + .../{AExecutionMode.cs => ExecutionMode.cs} | 2 +- ChocolArm64/State/FpExc.cs | 12 + ChocolArm64/State/FpType.cs | 11 + ChocolArm64/State/Fpcr.cs | 11 + ChocolArm64/State/Fpsr.cs | 8 + ChocolArm64/State/{APState.cs => PState.cs} | 10 +- ChocolArm64/State/Register.cs | 142 + .../{ARegisterSize.cs => RegisterSize.cs} | 6 +- .../{ARegisterType.cs => RegisterType.cs} | 2 +- .../State/{ARoundMode.cs => RoundMode.cs} | 4 +- ChocolArm64/TranslatedSub.cs | 150 + ...nslatedSubType.cs => TranslatedSubType.cs} | 2 +- ChocolArm64/Translation/AILBarrier.cs | 7 - ChocolArm64/Translation/AILBlock.cs | 76 - ChocolArm64/Translation/AILEmitter.cs | 188 - ChocolArm64/Translation/AILEmitterCtx.cs | 558 --- ChocolArm64/Translation/AILLabel.cs | 28 - ChocolArm64/Translation/AILOpCode.cs | 19 - ChocolArm64/Translation/AILOpCodeBranch.cs | 21 - ChocolArm64/Translation/AILOpCodeCall.cs | 20 - ChocolArm64/Translation/AILOpCodeConst.cs | 65 - ChocolArm64/Translation/AILOpCodeLoad.cs | 75 - ChocolArm64/Translation/AILOpCodeLog.cs | 17 - ChocolArm64/Translation/AILOpCodeStore.cs | 75 - ChocolArm64/Translation/ALocalAlloc.cs | 229 - ChocolArm64/Translation/IAILEmit.cs | 7 - ChocolArm64/Translation/IILEmit.cs | 7 + ChocolArm64/Translation/ILBarrier.cs | 7 + ChocolArm64/Translation/ILBlock.cs | 76 + ChocolArm64/Translation/ILEmitter.cs | 188 + ChocolArm64/Translation/ILEmitterCtx.cs | 554 +++ ChocolArm64/Translation/ILGeneratorEx.cs | 110 +- ChocolArm64/Translation/ILLabel.cs | 28 + ChocolArm64/Translation/ILOpCode.cs | 19 + ChocolArm64/Translation/ILOpCodeBranch.cs | 21 + ChocolArm64/Translation/ILOpCodeCall.cs | 20 + ChocolArm64/Translation/ILOpCodeConst.cs | 65 + ChocolArm64/Translation/ILOpCodeLoad.cs | 75 + ChocolArm64/Translation/ILOpCodeLog.cs | 17 + ChocolArm64/Translation/ILOpCodeStore.cs | 75 + .../Translation/{AIoType.cs => IoType.cs} | 2 +- ChocolArm64/Translation/LocalAlloc.cs | 229 + ChocolArm64/Translator.cs | 165 + ChocolArm64/TranslatorCache.cs | 165 + README.md | 3 - .../{ => Decoders}/Adpcm/AdpcmDecoder.cs | 0 .../Adpcm/AdpcmDecoderContext.cs | 0 Ryujinx.Audio/IAalOutput.cs | 16 +- .../Native/libsoundio/MarshalExtensions.cs | 23 + Ryujinx.Audio/Native/libsoundio/SoundIO.cs | 311 ++ .../Native/libsoundio/SoundIOBackend.cs | 15 + .../Native/libsoundio/SoundIOChannelArea.cs | 26 + .../Native/libsoundio/SoundIOChannelAreas.cs | 33 + .../Native/libsoundio/SoundIOChannelId.cs | 77 + .../Native/libsoundio/SoundIOChannelLayout.cs | 99 + .../Native/libsoundio/SoundIODevice.cs | 215 + .../Native/libsoundio/SoundIODeviceAim.cs | 9 + .../Native/libsoundio/SoundIOException.cs | 13 + .../Native/libsoundio/SoundIOFormat.cs | 26 + .../Native/libsoundio/SoundIOInStream.cs | 228 + .../Native/libsoundio/SoundIOOutStream.cs | 241 + .../Native/libsoundio/SoundIORingBuffer.cs | 61 + .../libsoundio/SoundIOSampleRateRange.cs | 15 + .../Native/libsoundio/libs/libsoundio.dll | Bin 0 -> 370899 bytes .../Native/libsoundio/libs/libsoundio.dylib | Bin 0 -> 106760 bytes .../Native/libsoundio/libs/libsoundio.so | Bin 0 -> 265888 bytes .../Native/libsoundio/libsoundio-interop.cs | 638 +++ Ryujinx.Audio/PlaybackState.cs | 9 + Ryujinx.Audio/Renderers/DummyAudioOut.cs | 63 + .../{ => Renderers}/OpenAL/OpenALAudioOut.cs | 165 +- .../Renderers/SoundIo/SoundIoAudioOut.cs | 248 + .../Renderers/SoundIo/SoundIoAudioTrack.cs | 560 +++ .../SoundIo/SoundIoAudioTrackPool.cs | 193 + .../Renderers/SoundIo/SoundIoBuffer.cs | 29 + .../Renderers/SoundIo/SoundIoRingBuffer.cs | 204 + Ryujinx.Audio/Ryujinx.Audio.csproj | 28 + .../Logging/LogClass.cs | 5 +- .../Logging/LogEventArgs.cs | 2 +- .../Logging/LogLevel.cs | 2 +- .../Logging/Logger.cs | 34 +- Ryujinx.Common/PerformanceCounter.cs | 65 + Ryujinx.Common/Ryujinx.Common.csproj | 16 + Ryujinx.Graphics/DmaPusher.cs | 190 + Ryujinx.Graphics/Gal/GalBlendEquation.cs | 8 +- Ryujinx.Graphics/Gal/GalBlendFactor.cs | 21 +- Ryujinx.Graphics/Gal/GalImage.cs | 48 +- Ryujinx.Graphics/Gal/GalImageFormat.cs | 122 +- Ryujinx.Graphics/Gal/GalMemoryLayout.cs | 8 + Ryujinx.Graphics/Gal/GalPipelineState.cs | 102 +- ...ameBufferFormat.cs => GalSurfaceFormat.cs} | 0 Ryujinx.Graphics/Gal/GalTextureFormat.cs | 78 +- Ryujinx.Graphics/Gal/GalVertexAttrib.cs | 11 +- Ryujinx.Graphics/Gal/GalVertexBinding.cs | 14 + Ryujinx.Graphics/Gal/GalZetaFormat.cs | 20 +- Ryujinx.Graphics/Gal/IGalPipeline.cs | 3 + Ryujinx.Graphics/Gal/IGalRasterizer.cs | 8 +- Ryujinx.Graphics/Gal/IGalRenderTarget.cs | 20 +- Ryujinx.Graphics/Gal/IGalTexture.cs | 8 +- Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs | 147 +- .../Gal/OpenGL/OGLCachedResource.cs | 43 +- Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs | 4 +- .../Gal/OpenGL/OGLEnumConverter.cs | 312 +- Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs | 37 +- Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs | 494 +- Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs | 55 +- .../Gal/OpenGL/OGLRenderTarget.cs | 564 +-- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 23 +- Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs | 182 +- Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 101 +- .../Gal/Shader/ShaderDecodeAlu.cs | 139 +- .../Gal/Shader/ShaderDecodeFlow.cs | 24 +- .../{ShaderDecode.cs => ShaderDecodeFunc.cs} | 2 +- .../Gal/Shader/ShaderDecodeHelper.cs | 10 +- .../Gal/Shader/ShaderDecodeMem.cs | 16 +- .../Gal/Shader/ShaderDecodeMove.cs | 42 +- .../Gal/Shader/ShaderDecodeSpecial.cs | 6 +- Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs | 48 +- Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs | 6 +- .../Gal/Shader/ShaderOpCodeTable.cs | 2 +- Ryujinx.Graphics/Gal/ShaderDumper.cs | 14 +- Ryujinx.Graphics/GpuMethodCall.cs | 24 + Ryujinx.Graphics/GpuResourceManager.cs | 145 + Ryujinx.Graphics/INvGpuEngine.cs | 2 +- Ryujinx.Graphics/MacroInterpreter.cs | 16 +- Ryujinx.Graphics/Memory/NvGpuPBEntry.cs | 23 - Ryujinx.Graphics/Memory/NvGpuPushBuffer.cs | 101 - Ryujinx.Graphics/Memory/NvGpuVmm.cs | 6 +- Ryujinx.Graphics/Memory/NvGpuVmmCache.cs | 335 +- Ryujinx.Graphics/NvGpu.cs | 25 +- .../{Memory => }/NvGpuBufferType.cs | 2 +- Ryujinx.Graphics/NvGpuEngine.cs | 4 +- Ryujinx.Graphics/NvGpuEngine2d.cs | 210 +- Ryujinx.Graphics/NvGpuEngine2dReg.cs | 16 +- Ryujinx.Graphics/NvGpuEngine3d.cs | 470 +- Ryujinx.Graphics/NvGpuEngine3dReg.cs | 13 +- Ryujinx.Graphics/NvGpuEngineDma.cs | 143 - Ryujinx.Graphics/NvGpuEngineM2mf.cs | 187 + ...uEngineDmaReg.cs => NvGpuEngineM2mfReg.cs} | 5 +- Ryujinx.Graphics/NvGpuEngineP2mf.cs | 161 + Ryujinx.Graphics/NvGpuEngineP2mfReg.cs | 17 + Ryujinx.Graphics/NvGpuFifo.cs | 168 +- Ryujinx.Graphics/NvGpuMethod.cs | 2 +- Ryujinx.Graphics/QuadHelper.cs | 81 + Ryujinx.Graphics/Ryujinx.Graphics.csproj | 1 + Ryujinx.Graphics/Texture/ImageConverter.cs | 24 - Ryujinx.Graphics/Texture/ImageUtils.cs | 589 ++- Ryujinx.Graphics/Texture/TextureFactory.cs | 61 +- Ryujinx.Graphics/Texture/TextureHelper.cs | 37 +- Ryujinx.Graphics/Texture/TextureInfo.cs | 60 - Ryujinx.Graphics/Texture/TextureReader.cs | 398 -- Ryujinx.Graphics/Texture/TextureWriter.cs | 35 - Ryujinx.Graphics/ValueRange.cs | 17 + Ryujinx.Graphics/ValueRangeSet.cs | 234 + Ryujinx.HLE/FileSystem/SaveHelper.cs | 4 +- Ryujinx.HLE/FileSystem/SaveInfo.cs | 6 +- Ryujinx.HLE/HOS/Diagnostics/Demangler.cs | 416 -- .../Ast/ArraySubscriptingExpression.cs | 25 + .../Diagnostics/Demangler/Ast/ArrayType.cs | 59 + .../HOS/Diagnostics/Demangler/Ast/BaseNode.cs | 113 + .../Demangler/Ast/BinaryExpression.cs | 41 + .../Demangler/Ast/BracedExpression.cs | 40 + .../Demangler/Ast/BracedRangeExpression.cs | 34 + .../Demangler/Ast/CallExpression.cs | 24 + .../Demangler/Ast/CastExpression.cs | 28 + .../Demangler/Ast/ConditionalExpression.cs | 29 + .../Demangler/Ast/ConversionExpression.cs | 24 + .../Demangler/Ast/ConversionOperatorType.cs | 15 + .../Demangler/Ast/CtorDtorNameType.cs | 24 + .../Demangler/Ast/CtorVtableSpecialName.cs | 24 + .../Demangler/Ast/DeleteExpression.cs | 33 + .../HOS/Diagnostics/Demangler/Ast/DtorName.cs | 15 + .../Demangler/Ast/DynamicExceptionSpec.cs | 16 + .../Demangler/Ast/ElaboratedType.cs | 21 + .../Demangler/Ast/EnclosedExpression.cs | 25 + .../Demangler/Ast/EncodedFunction.cs | 77 + .../Demangler/Ast/FoldExpression.cs | 48 + .../Demangler/Ast/ForwardTemplateReference.cs | 36 + .../Demangler/Ast/FunctionParameter.cs | 24 + .../Diagnostics/Demangler/Ast/FunctionType.cs | 61 + .../Demangler/Ast/GlobalQualifiedName.cs | 15 + .../Demangler/Ast/InitListExpression.cs | 29 + .../Demangler/Ast/IntegerCastExpression.cs | 22 + .../Demangler/Ast/IntegerLiteral.cs | 41 + .../Demangler/Ast/LiteralOperator.cs | 16 + .../Diagnostics/Demangler/Ast/LocalName.cs | 23 + .../Demangler/Ast/MemberExpression.cs | 25 + .../HOS/Diagnostics/Demangler/Ast/NameType.cs | 29 + .../Ast/NameTypeWithTemplateArguments.cs | 27 + .../Diagnostics/Demangler/Ast/NestedName.cs | 26 + .../Demangler/Ast/NewExpression.cs | 55 + .../Diagnostics/Demangler/Ast/NodeArray.cs | 30 + .../Diagnostics/Demangler/Ast/NoexceptSpec.cs | 16 + .../Demangler/Ast/PackedTemplateParameter.cs | 39 + .../Ast/PackedTemplateParameterExpansion.cs | 24 + .../Diagnostics/Demangler/Ast/ParentNode.cs | 17 + .../Diagnostics/Demangler/Ast/PointerType.cs | 45 + .../Demangler/Ast/PostfixExpression.cs | 22 + .../Demangler/Ast/PostfixQualifiedType.cs | 20 + .../Demangler/Ast/PrefixExpression.cs | 22 + .../Demangler/Ast/QualifiedName.cs | 23 + .../Diagnostics/Demangler/Ast/Qualifier.cs | 120 + .../Demangler/Ast/ReferenceType.cs | 47 + .../Diagnostics/Demangler/Ast/SpecialName.cs | 20 + .../Demangler/Ast/SpecialSubstitution.cs | 89 + .../Demangler/Ast/StdQualifiedName.cs | 15 + .../Demangler/Ast/TemplateArguments.cs | 26 + .../Demangler/Ast/ThrowExpression.cs | 20 + .../HOS/Diagnostics/Demangler/Demangler.cs | 3367 +++++++++++++ Ryujinx.HLE/HOS/Homebrew.cs | 18 +- Ryujinx.HLE/HOS/Horizon.cs | 223 +- Ryujinx.HLE/HOS/Ipc/IpcHandler.cs | 17 +- Ryujinx.HLE/HOS/Kernel/AddressArbiter.cs | 111 - Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs | 9 + Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs | 29 + Ryujinx.HLE/HOS/Kernel/HleScheduler.cs | 149 + .../HOS/Kernel/IKFutureSchedulerObject.cs | 7 + Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs | 678 +++ Ryujinx.HLE/HOS/Kernel/KCoreContext.cs | 66 + Ryujinx.HLE/HOS/Kernel/KEvent.cs | 12 +- Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs | 17 + Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs | 102 +- Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs | 173 +- Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs | 370 -- Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs | 62 + Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs | 93 + Ryujinx.HLE/HOS/Kernel/KScheduler.cs | 228 + Ryujinx.HLE/HOS/Kernel/KSchedulingData.cs | 207 + Ryujinx.HLE/HOS/Kernel/KSynchronization.cs | 135 + .../HOS/Kernel/KSynchronizationObject.cs | 36 +- Ryujinx.HLE/HOS/Kernel/KThread.cs | 901 +++- Ryujinx.HLE/HOS/Kernel/KTimeManager.cs | 134 + Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs | 22 + Ryujinx.HLE/HOS/Kernel/KernelErr.cs | 4 +- Ryujinx.HLE/HOS/Kernel/KernelResult.cs | 10 + Ryujinx.HLE/HOS/Kernel/NsTimeConverter.cs | 19 - Ryujinx.HLE/HOS/Kernel/SchedulerThread.cs | 48 - Ryujinx.HLE/HOS/Kernel/SignalType.cs | 9 + Ryujinx.HLE/HOS/Kernel/SvcHandler.cs | 69 +- Ryujinx.HLE/HOS/Kernel/SvcMemory.cs | 122 +- Ryujinx.HLE/HOS/Kernel/SvcSystem.cs | 340 +- Ryujinx.HLE/HOS/Kernel/SvcThread.cs | 347 +- Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs | 576 +-- Ryujinx.HLE/HOS/Kernel/ThreadQueue.cs | 158 - Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs | 15 + Ryujinx.HLE/HOS/Process.cs | 294 +- Ryujinx.HLE/HOS/ServiceCtx.cs | 32 +- .../HOS/Services/Acc/IAccountService.cs | 67 +- .../Services/Acc/IManagerForApplication.cs | 19 +- Ryujinx.HLE/HOS/Services/Acc/IProfile.cs | 7 +- .../HOS/Services/Am/IApplicationFunctions.cs | 12 +- .../HOS/Services/Am/IApplicationProxy.cs | 4 +- .../HOS/Services/Am/IAudioController.cs | 12 +- .../HOS/Services/Am/ICommonStateGetter.cs | 21 +- .../HOS/Services/Am/IHomeMenuFunctions.cs | 16 +- .../HOS/Services/Am/ILibraryAppletAccessor.cs | 24 +- .../HOS/Services/Am/ILibraryAppletCreator.cs | 2 +- .../HOS/Services/Am/ISelfController.cs | 64 +- .../HOS/Services/Am/ISystemAppletProxy.cs | 6 +- .../HOS/Services/Am/IWindowController.cs | 6 +- Ryujinx.HLE/HOS/Services/Apm/ISession.cs | 4 +- Ryujinx.HLE/HOS/Services/Aud/AudErr.cs | 1 + .../HOS/Services/Aud/AudioOut/IAudioOut.cs | 9 +- .../Aud/AudioRenderer/IAudioRenderer.cs | 129 +- .../Aud/AudioRenderer/VoiceContext.cs | 11 +- Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs | 46 +- .../HOS/Services/Aud/IAudioOutManager.cs | 16 +- .../HOS/Services/Aud/IAudioRendererManager.cs | 38 +- .../HOS/Services/Aud/IHardwareOpusDecoder.cs | 91 + .../Aud/IHardwareOpusDecoderManager.cs | 72 + Ryujinx.HLE/HOS/Services/Bsd/BsdError.cs | 8 - Ryujinx.HLE/HOS/Services/Bsd/BsdIoctl.cs | 7 + Ryujinx.HLE/HOS/Services/Bsd/BsdSocket.cs | 5 - Ryujinx.HLE/HOS/Services/Bsd/IClient.cs | 1267 +++-- Ryujinx.HLE/HOS/Services/Bsd/PollEvent.cs | 28 + .../HOS/Services/Friend/IFriendService.cs | 63 +- .../Services/Friend/IFriendServiceTypes.cs | 20 + .../HOS/Services/FspSrv/IFileSystem.cs | 5 - .../HOS/Services/FspSrv/IFileSystemProxy.cs | 4 +- Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs | 9 +- Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs | 41 + Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs | 21 + Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs | 29 + .../HOS/Services/Hid/IAppletResource.cs | 6 +- Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 1408 +++++- Ryujinx.HLE/HOS/Services/IpcService.cs | 11 +- .../HOS/Services/Irs/IIrSensorServer.cs | 44 + Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs | 457 ++ Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs | 18 + Ryujinx.HLE/HOS/Services/Lm/ILogger.cs | 12 +- Ryujinx.HLE/HOS/Services/Mm/IRequest.cs | 27 +- Ryujinx.HLE/HOS/Services/Nfp/IUser.cs | 42 +- Ryujinx.HLE/HOS/Services/Nfp/IUserManager.cs | 2 +- .../HOS/Services/Nifm/IGeneralService.cs | 8 +- Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs | 45 +- .../HOS/Services/Ns/IAddOnContentManager.cs | 6 +- Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs | 34 +- .../HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs | 42 +- .../HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs | 24 +- .../Nv/NvHostChannel/NvHostChannelIoctl.cs | 34 +- .../Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs | 26 +- .../HOS/Services/Nv/NvMap/NvMapIoctl.cs | 46 +- .../Services/Pctl/IParentalControlService.cs | 4 +- .../HOS/Services/Pl/ISharedFontManager.cs | 7 +- .../HOS/Services/Prepo/IPrepoService.cs | 4 +- Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs | 60 + Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs | 96 + Ryujinx.HLE/HOS/Services/ServiceFactory.cs | 27 +- .../HOS/Services/Set/ISystemSettingsServer.cs | 7 +- Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs | 22 + .../HOS/Services/Sfdnsres/IResolver.cs | 381 +- .../HOS/Services/Sfdnsres/NetDBError.cs | 13 + Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 8 +- Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs | 20 + Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs | 19 +- .../HOS/Services/Time/ITimeZoneService.cs | 14 +- .../Services/Vi/IApplicationDisplayService.cs | 17 +- .../HOS/Services/Vi/IHOSBinderDriver.cs | 17 +- .../HOS/Services/Vi/IManagerDisplayService.cs | 10 +- .../HOS/Services/Vi/ISystemDisplayService.cs | 6 +- Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs | 86 +- Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs | 24 +- Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs | 24 +- Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs | 21 + Ryujinx.HLE/HOS/SystemState/UserId.cs | 76 - Ryujinx.HLE/HOS/SystemState/UserProfile.cs | 7 +- Ryujinx.HLE/Hid/Hid.cs | 86 +- Ryujinx.HLE/Loaders/Executable.cs | 65 +- .../Loaders/Executables/IExecutable.cs | 3 + Ryujinx.HLE/Loaders/Executables/Nro.cs | 9 +- Ryujinx.HLE/Loaders/Executables/Nso.cs | 6 + Ryujinx.HLE/Memory/ArenaAllocator.cs | 48 +- Ryujinx.HLE/Ryujinx.HLE.csproj | 7 +- Ryujinx.HLE/RyujinxProfileImage.jpg | Bin 5394 -> 52969 bytes Ryujinx.HLE/Switch.cs | 9 +- Ryujinx.HLE/Utilities/LinuxError.cs | 152 + Ryujinx.HLE/Utilities/StructReader.cs | 8 +- Ryujinx.HLE/Utilities/StructWriter.cs | 6 +- Ryujinx.HLE/Utilities/UInt128.cs | 45 + Ryujinx.HLE/Utilities/WSAError.cs | 135 + Ryujinx.Tests.Unicorn/MemoryPermission.cs | 2 - Ryujinx.Tests.Unicorn/Native/ArmRegister.cs | 2 - Ryujinx.Tests.Unicorn/Native/Interface.cs | 1 - Ryujinx.Tests.Unicorn/Native/UnicornArch.cs | 2 - Ryujinx.Tests.Unicorn/Native/UnicornMode.cs | 2 - Ryujinx.Tests.Unicorn/UnicornAArch64.cs | 1 - Ryujinx.Tests.Unicorn/UnicornError.cs | 2 - Ryujinx.Tests/Cpu/CpuTest.cs | 637 ++- Ryujinx.Tests/Cpu/CpuTestAlu.cs | 158 +- Ryujinx.Tests/Cpu/CpuTestAluImm.cs | 356 +- Ryujinx.Tests/Cpu/CpuTestAluRs.cs | 1044 ++-- Ryujinx.Tests/Cpu/CpuTestAluRx.cs | 620 ++- Ryujinx.Tests/Cpu/CpuTestBfm.cs | 104 +- Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs | 60 +- Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs | 76 +- Ryujinx.Tests/Cpu/CpuTestCsel.cs | 164 +- Ryujinx.Tests/Cpu/CpuTestMisc.cs | 56 +- Ryujinx.Tests/Cpu/CpuTestMov.cs | 80 +- Ryujinx.Tests/Cpu/CpuTestMul.cs | 216 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 2337 +++++---- Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs | 417 +- Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs | 407 -- Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs | 140 +- Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs | 43 - Ryujinx.Tests/Cpu/CpuTestSimdExt.cs | 73 + Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs | 178 + Ryujinx.Tests/Cpu/CpuTestSimdIns.cs | 377 ++ Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 4338 ++++++++++------- Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs | 161 +- Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs | 446 ++ Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs | 586 ++- Ryujinx.Tests/Ryujinx.Tests.csproj | 6 +- Ryujinx.sln | 16 +- Ryujinx/Config.cs | 204 +- Ryujinx/{Ui => }/Program.cs | 60 +- Ryujinx/Ryujinx.conf | 6 + Ryujinx/Ryujinx.csproj | 4 +- Ryujinx/Ui/ConsoleLog.cs | 46 +- Ryujinx/Ui/GLScreen.cs | 221 +- Ryujinx/Ui/JoyConController.cs | 178 +- Ryujinx/Ui/JoyConKeyboard.cs | 82 +- 591 files changed, 49767 insertions(+), 27318 deletions(-) delete mode 100644 ChocolArm64/ABitUtils.cs delete mode 100644 ChocolArm64/AOpCodeTable.cs delete mode 100644 ChocolArm64/AOptimizations.cs delete mode 100644 ChocolArm64/AThread.cs delete mode 100644 ChocolArm64/ATranslatedSub.cs delete mode 100644 ChocolArm64/ATranslator.cs create mode 100644 ChocolArm64/BitUtils.cs create mode 100644 ChocolArm64/CpuThread.cs delete mode 100644 ChocolArm64/Decoder/ABlock.cs delete mode 100644 ChocolArm64/Decoder/ACond.cs delete mode 100644 ChocolArm64/Decoder/ADecoder.cs delete mode 100644 ChocolArm64/Decoder/ADecoderHelper.cs delete mode 100644 ChocolArm64/Decoder/AOpCode.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeAdr.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeAlu.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeAluImm.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeAluRs.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeAluRx.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeBImm.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeBImmAl.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeBImmCmp.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeBImmCond.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeBImmTest.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeBReg.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeBfm.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeCcmp.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeCcmpImm.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeCcmpReg.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeCsel.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeException.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeMem.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeMemEx.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeMemPair.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeMemReg.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeMov.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeMul.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimd.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdCvt.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdExt.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdFcond.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdFmov.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdImm.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdIns.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdMemImm.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdMemLit.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdMemPair.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdMemReg.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdMemSs.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdReg.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdRegElem.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdShImm.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSimdTbl.cs delete mode 100644 ChocolArm64/Decoder/AOpCodeSystem.cs delete mode 100644 ChocolArm64/Decoder/IAOpCode.cs delete mode 100644 ChocolArm64/Decoder/IAOpCodeAlu.cs delete mode 100644 ChocolArm64/Decoder/IAOpCodeAluImm.cs delete mode 100644 ChocolArm64/Decoder/IAOpCodeAluRs.cs delete mode 100644 ChocolArm64/Decoder/IAOpCodeAluRx.cs delete mode 100644 ChocolArm64/Decoder/IAOpCodeCond.cs delete mode 100644 ChocolArm64/Decoder/IAOpCodeSimd.cs delete mode 100644 ChocolArm64/Decoder32/A32OpCode.cs delete mode 100644 ChocolArm64/Decoder32/A32OpCodeBImmAl.cs create mode 100644 ChocolArm64/Decoders/Block.cs create mode 100644 ChocolArm64/Decoders/Cond.cs rename ChocolArm64/{Decoder/ADataOp.cs => Decoders/DataOp.cs} (70%) create mode 100644 ChocolArm64/Decoders/Decoder.cs create mode 100644 ChocolArm64/Decoders/DecoderHelper.cs create mode 100644 ChocolArm64/Decoders/IOpCode64.cs create mode 100644 ChocolArm64/Decoders/IOpCodeAlu64.cs create mode 100644 ChocolArm64/Decoders/IOpCodeAluImm64.cs create mode 100644 ChocolArm64/Decoders/IOpCodeAluRs64.cs create mode 100644 ChocolArm64/Decoders/IOpCodeAluRx64.cs create mode 100644 ChocolArm64/Decoders/IOpCodeCond64.cs rename ChocolArm64/{Decoder/IAOpCodeLit.cs => Decoders/IOpCodeLit64.cs} (70%) create mode 100644 ChocolArm64/Decoders/IOpCodeSimd64.cs rename ChocolArm64/{Decoder/AIntType.cs => Decoders/IntType.cs} (78%) create mode 100644 ChocolArm64/Decoders/OpCode64.cs create mode 100644 ChocolArm64/Decoders/OpCodeAdr64.cs create mode 100644 ChocolArm64/Decoders/OpCodeAlu64.cs create mode 100644 ChocolArm64/Decoders/OpCodeAluImm64.cs create mode 100644 ChocolArm64/Decoders/OpCodeAluRs64.cs create mode 100644 ChocolArm64/Decoders/OpCodeAluRx64.cs create mode 100644 ChocolArm64/Decoders/OpCodeBImm64.cs create mode 100644 ChocolArm64/Decoders/OpCodeBImmAl64.cs create mode 100644 ChocolArm64/Decoders/OpCodeBImmCmp64.cs create mode 100644 ChocolArm64/Decoders/OpCodeBImmCond64.cs create mode 100644 ChocolArm64/Decoders/OpCodeBImmTest64.cs create mode 100644 ChocolArm64/Decoders/OpCodeBReg64.cs create mode 100644 ChocolArm64/Decoders/OpCodeBfm64.cs create mode 100644 ChocolArm64/Decoders/OpCodeCcmp64.cs create mode 100644 ChocolArm64/Decoders/OpCodeCcmpImm64.cs create mode 100644 ChocolArm64/Decoders/OpCodeCcmpReg64.cs create mode 100644 ChocolArm64/Decoders/OpCodeCsel64.cs create mode 100644 ChocolArm64/Decoders/OpCodeException64.cs create mode 100644 ChocolArm64/Decoders/OpCodeMem64.cs create mode 100644 ChocolArm64/Decoders/OpCodeMemEx64.cs rename ChocolArm64/{Decoder/AOpCodeMemImm.cs => Decoders/OpCodeMemImm64.cs} (62%) rename ChocolArm64/{Decoder/AOpCodeMemLit.cs => Decoders/OpCodeMemLit64.cs} (63%) create mode 100644 ChocolArm64/Decoders/OpCodeMemPair64.cs create mode 100644 ChocolArm64/Decoders/OpCodeMemReg64.cs create mode 100644 ChocolArm64/Decoders/OpCodeMov64.cs create mode 100644 ChocolArm64/Decoders/OpCodeMul64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimd64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdCvt64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdExt64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdFcond64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdFmov64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdImm64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdIns64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdMemImm64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdMemLit64.cs rename ChocolArm64/{Decoder/AOpCodeSimdMemMs.cs => Decoders/OpCodeSimdMemMs64.cs} (55%) create mode 100644 ChocolArm64/Decoders/OpCodeSimdMemPair64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdMemReg64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdMemSs64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdReg64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdRegElem64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdRegElemF64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdShImm64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSimdTbl64.cs create mode 100644 ChocolArm64/Decoders/OpCodeSystem64.cs rename ChocolArm64/{Decoder/AShiftType.cs => Decoders/ShiftType.cs} (56%) create mode 100644 ChocolArm64/Decoders32/A32OpCode.cs create mode 100644 ChocolArm64/Decoders32/A32OpCodeBImmAl.cs delete mode 100644 ChocolArm64/Events/ACpuTraceEventArgs.cs delete mode 100644 ChocolArm64/Events/AInstExceptionEventArgs.cs delete mode 100644 ChocolArm64/Events/AInstUndefinedEventArgs.cs create mode 100644 ChocolArm64/Events/CpuTraceEventArgs.cs create mode 100644 ChocolArm64/Events/InstExceptionEventArgs.cs create mode 100644 ChocolArm64/Events/InstUndefinedEventArgs.cs create mode 100644 ChocolArm64/Events/InvalidAccessEventArgs.cs delete mode 100644 ChocolArm64/Instruction/AInst.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitAlu.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitAluHelper.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitBfm.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitCcmp.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitCsel.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitException.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitFlow.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitHash.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitMemory.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitMemoryEx.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitMemoryHelper.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitMove.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitMul.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdCmp.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdCrypto.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdCvt.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdHash.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdHelper.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdLogical.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdMemory.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdMove.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSimdShift.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitSystem.cs delete mode 100644 ChocolArm64/Instruction/AInstEmitter.cs delete mode 100644 ChocolArm64/Instruction/AInstInterpreter.cs delete mode 100644 ChocolArm64/Instruction/ASoftFallback.cs delete mode 100644 ChocolArm64/Instruction/ASoftFloat.cs delete mode 100644 ChocolArm64/Instruction/AVectorHelper.cs delete mode 100644 ChocolArm64/Instruction32/A32InstInterpretFlow.cs delete mode 100644 ChocolArm64/Instruction32/A32InstInterpretHelper.cs rename ChocolArm64/{Instruction/ACryptoHelper.cs => Instructions/CryptoHelper.cs} (75%) create mode 100644 ChocolArm64/Instructions/Inst.cs create mode 100644 ChocolArm64/Instructions/InstEmitAlu.cs create mode 100644 ChocolArm64/Instructions/InstEmitAluHelper.cs create mode 100644 ChocolArm64/Instructions/InstEmitBfm.cs create mode 100644 ChocolArm64/Instructions/InstEmitCcmp.cs create mode 100644 ChocolArm64/Instructions/InstEmitCsel.cs create mode 100644 ChocolArm64/Instructions/InstEmitException.cs create mode 100644 ChocolArm64/Instructions/InstEmitFlow.cs create mode 100644 ChocolArm64/Instructions/InstEmitHash.cs create mode 100644 ChocolArm64/Instructions/InstEmitMemory.cs create mode 100644 ChocolArm64/Instructions/InstEmitMemoryEx.cs create mode 100644 ChocolArm64/Instructions/InstEmitMemoryHelper.cs create mode 100644 ChocolArm64/Instructions/InstEmitMove.cs create mode 100644 ChocolArm64/Instructions/InstEmitMul.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdArithmetic.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdCmp.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdCrypto.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdCvt.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdHash.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdHelper.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdLogical.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdMemory.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdMove.cs create mode 100644 ChocolArm64/Instructions/InstEmitSimdShift.cs create mode 100644 ChocolArm64/Instructions/InstEmitSystem.cs create mode 100644 ChocolArm64/Instructions/InstEmitter.cs create mode 100644 ChocolArm64/Instructions/InstInterpreter.cs create mode 100644 ChocolArm64/Instructions/SoftFallback.cs create mode 100644 ChocolArm64/Instructions/SoftFloat.cs create mode 100644 ChocolArm64/Instructions/VectorHelper.cs rename ChocolArm64/{Instruction32 => Instructions32}/A32InstInterpretAlu.cs (61%) create mode 100644 ChocolArm64/Instructions32/A32InstInterpretFlow.cs create mode 100644 ChocolArm64/Instructions32/A32InstInterpretHelper.cs delete mode 100644 ChocolArm64/Memory/AMemory.cs delete mode 100644 ChocolArm64/Memory/AMemoryHelper.cs delete mode 100644 ChocolArm64/Memory/IAMemory.cs create mode 100644 ChocolArm64/Memory/IMemory.cs create mode 100644 ChocolArm64/Memory/MemoryHelper.cs create mode 100644 ChocolArm64/Memory/MemoryManager.cs create mode 100644 ChocolArm64/OpCodeTable.cs create mode 100644 ChocolArm64/Optimizations.cs delete mode 100644 ChocolArm64/State/ARegister.cs delete mode 100644 ChocolArm64/State/AThreadState.cs create mode 100644 ChocolArm64/State/CpuThreadState.cs rename ChocolArm64/State/{AExecutionMode.cs => ExecutionMode.cs} (76%) create mode 100644 ChocolArm64/State/FpExc.cs create mode 100644 ChocolArm64/State/FpType.cs create mode 100644 ChocolArm64/State/Fpcr.cs create mode 100644 ChocolArm64/State/Fpsr.cs rename ChocolArm64/State/{APState.cs => PState.cs} (73%) create mode 100644 ChocolArm64/State/Register.cs rename ChocolArm64/State/{ARegisterSize.cs => RegisterSize.cs} (57%) rename ChocolArm64/State/{ARegisterType.cs => RegisterType.cs} (78%) rename ChocolArm64/State/{ARoundMode.cs => RoundMode.cs} (86%) create mode 100644 ChocolArm64/TranslatedSub.cs rename ChocolArm64/{ATranslatedSubType.cs => TranslatedSubType.cs} (72%) delete mode 100644 ChocolArm64/Translation/AILBarrier.cs delete mode 100644 ChocolArm64/Translation/AILBlock.cs delete mode 100644 ChocolArm64/Translation/AILEmitter.cs delete mode 100644 ChocolArm64/Translation/AILEmitterCtx.cs delete mode 100644 ChocolArm64/Translation/AILLabel.cs delete mode 100644 ChocolArm64/Translation/AILOpCode.cs delete mode 100644 ChocolArm64/Translation/AILOpCodeBranch.cs delete mode 100644 ChocolArm64/Translation/AILOpCodeCall.cs delete mode 100644 ChocolArm64/Translation/AILOpCodeConst.cs delete mode 100644 ChocolArm64/Translation/AILOpCodeLoad.cs delete mode 100644 ChocolArm64/Translation/AILOpCodeLog.cs delete mode 100644 ChocolArm64/Translation/AILOpCodeStore.cs delete mode 100644 ChocolArm64/Translation/ALocalAlloc.cs delete mode 100644 ChocolArm64/Translation/IAILEmit.cs create mode 100644 ChocolArm64/Translation/IILEmit.cs create mode 100644 ChocolArm64/Translation/ILBarrier.cs create mode 100644 ChocolArm64/Translation/ILBlock.cs create mode 100644 ChocolArm64/Translation/ILEmitter.cs create mode 100644 ChocolArm64/Translation/ILEmitterCtx.cs create mode 100644 ChocolArm64/Translation/ILLabel.cs create mode 100644 ChocolArm64/Translation/ILOpCode.cs create mode 100644 ChocolArm64/Translation/ILOpCodeBranch.cs create mode 100644 ChocolArm64/Translation/ILOpCodeCall.cs create mode 100644 ChocolArm64/Translation/ILOpCodeConst.cs create mode 100644 ChocolArm64/Translation/ILOpCodeLoad.cs create mode 100644 ChocolArm64/Translation/ILOpCodeLog.cs create mode 100644 ChocolArm64/Translation/ILOpCodeStore.cs rename ChocolArm64/Translation/{AIoType.cs => IoType.cs} (90%) create mode 100644 ChocolArm64/Translation/LocalAlloc.cs create mode 100644 ChocolArm64/Translator.cs create mode 100644 ChocolArm64/TranslatorCache.cs rename Ryujinx.Audio/{ => Decoders}/Adpcm/AdpcmDecoder.cs (100%) rename Ryujinx.Audio/{ => Decoders}/Adpcm/AdpcmDecoderContext.cs (100%) create mode 100644 Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIO.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOBackend.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOChannelArea.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOChannelAreas.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOChannelId.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOChannelLayout.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIODevice.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIODeviceAim.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOException.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOFormat.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOInStream.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIORingBuffer.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/SoundIOSampleRateRange.cs create mode 100644 Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll create mode 100644 Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib create mode 100644 Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so create mode 100644 Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs create mode 100644 Ryujinx.Audio/Renderers/DummyAudioOut.cs rename Ryujinx.Audio/{ => Renderers}/OpenAL/OpenALAudioOut.cs (72%) create mode 100644 Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs create mode 100644 Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs create mode 100644 Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs create mode 100644 Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs create mode 100644 Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs rename {Ryujinx.HLE => Ryujinx.Common}/Logging/LogClass.cs (88%) rename {Ryujinx.HLE => Ryujinx.Common}/Logging/LogEventArgs.cs (92%) rename {Ryujinx.HLE => Ryujinx.Common}/Logging/LogLevel.cs (77%) rename {Ryujinx.HLE => Ryujinx.Common}/Logging/Logger.cs (57%) create mode 100644 Ryujinx.Common/PerformanceCounter.cs create mode 100644 Ryujinx.Common/Ryujinx.Common.csproj create mode 100644 Ryujinx.Graphics/DmaPusher.cs create mode 100644 Ryujinx.Graphics/Gal/GalMemoryLayout.cs rename Ryujinx.Graphics/Gal/{GalFrameBufferFormat.cs => GalSurfaceFormat.cs} (100%) create mode 100644 Ryujinx.Graphics/Gal/GalVertexBinding.cs rename Ryujinx.Graphics/Gal/Shader/{ShaderDecode.cs => ShaderDecodeFunc.cs} (83%) create mode 100644 Ryujinx.Graphics/GpuMethodCall.cs create mode 100644 Ryujinx.Graphics/GpuResourceManager.cs delete mode 100644 Ryujinx.Graphics/Memory/NvGpuPBEntry.cs delete mode 100644 Ryujinx.Graphics/Memory/NvGpuPushBuffer.cs rename Ryujinx.Graphics/{Memory => }/NvGpuBufferType.cs (79%) delete mode 100644 Ryujinx.Graphics/NvGpuEngineDma.cs create mode 100644 Ryujinx.Graphics/NvGpuEngineM2mf.cs rename Ryujinx.Graphics/{NvGpuEngineDmaReg.cs => NvGpuEngineM2mfReg.cs} (81%) create mode 100644 Ryujinx.Graphics/NvGpuEngineP2mf.cs create mode 100644 Ryujinx.Graphics/NvGpuEngineP2mfReg.cs create mode 100644 Ryujinx.Graphics/QuadHelper.cs delete mode 100644 Ryujinx.Graphics/Texture/ImageConverter.cs delete mode 100644 Ryujinx.Graphics/Texture/TextureInfo.cs delete mode 100644 Ryujinx.Graphics/Texture/TextureReader.cs delete mode 100644 Ryujinx.Graphics/Texture/TextureWriter.cs create mode 100644 Ryujinx.Graphics/ValueRange.cs create mode 100644 Ryujinx.Graphics/ValueRangeSet.cs delete mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs create mode 100644 Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs delete mode 100644 Ryujinx.HLE/HOS/Kernel/AddressArbiter.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/HleScheduler.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/IKFutureSchedulerObject.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KCoreContext.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs delete mode 100644 Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KScheduler.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KSchedulingData.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KSynchronization.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KTimeManager.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KernelResult.cs delete mode 100644 Ryujinx.HLE/HOS/Kernel/NsTimeConverter.cs delete mode 100644 Ryujinx.HLE/HOS/Kernel/SchedulerThread.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/SignalType.cs delete mode 100644 Ryujinx.HLE/HOS/Kernel/ThreadQueue.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs create mode 100644 Ryujinx.HLE/HOS/Services/Aud/IHardwareOpusDecoder.cs create mode 100644 Ryujinx.HLE/HOS/Services/Aud/IHardwareOpusDecoderManager.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Bsd/BsdError.cs create mode 100644 Ryujinx.HLE/HOS/Services/Bsd/BsdIoctl.cs create mode 100644 Ryujinx.HLE/HOS/Services/Bsd/PollEvent.cs create mode 100644 Ryujinx.HLE/HOS/Services/Friend/IFriendServiceTypes.cs create mode 100644 Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs create mode 100644 Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs create mode 100644 Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs create mode 100644 Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs create mode 100644 Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs create mode 100644 Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs create mode 100644 Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs create mode 100644 Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs create mode 100644 Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs create mode 100644 Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs create mode 100644 Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs delete mode 100644 Ryujinx.HLE/HOS/SystemState/UserId.cs create mode 100644 Ryujinx.HLE/Utilities/LinuxError.cs create mode 100644 Ryujinx.HLE/Utilities/UInt128.cs create mode 100644 Ryujinx.HLE/Utilities/WSAError.cs delete mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs delete mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs create mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdExt.cs create mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs create mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdIns.cs create mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs rename Ryujinx/{Ui => }/Program.cs (51%) diff --git a/ChocolArm64/ABitUtils.cs b/ChocolArm64/ABitUtils.cs deleted file mode 100644 index dd41623564..0000000000 --- a/ChocolArm64/ABitUtils.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace ChocolArm64 -{ - static class ABitUtils - { - public static int CountBitsSet(long Value) - { - int Count = 0; - - for (int Bit = 0; Bit < 64; Bit++) - { - Count += (int)(Value >> Bit) & 1; - } - - return Count; - } - - public static int HighestBitSet32(int Value) - { - for (int Bit = 31; Bit >= 0; Bit--) - { - if (((Value >> Bit) & 1) != 0) - { - return Bit; - } - } - - return -1; - } - - private static readonly sbyte[] HbsNibbleTbl = { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; - - public static int HighestBitSetNibble(int Value) => HbsNibbleTbl[Value & 0b1111]; - - public static long Replicate(long Bits, int Size) - { - long Output = 0; - - for (int Bit = 0; Bit < 64; Bit += Size) - { - Output |= Bits << Bit; - } - - return Output; - } - - public static long FillWithOnes(int Bits) - { - return Bits == 64 ? -1L : (1L << Bits) - 1; - } - - public static long RotateRight(long Bits, int Shift, int Size) - { - return (Bits >> Shift) | (Bits << (Size - Shift)); - } - - public static bool IsPow2(int Value) - { - return Value != 0 && (Value & (Value - 1)) == 0; - } - } -} diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs deleted file mode 100644 index b053334f3c..0000000000 --- a/ChocolArm64/AOpCodeTable.cs +++ /dev/null @@ -1,684 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Decoder32; -using ChocolArm64.Instruction; -using ChocolArm64.Instruction32; -using ChocolArm64.State; -using System; -using System.Collections.Generic; - -namespace ChocolArm64 -{ - static class AOpCodeTable - { - static AOpCodeTable() - { -#region "OpCode Table (AArch32)" - //Integer - SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.B, typeof(A32OpCodeBImmAl)); - SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl, typeof(A32OpCodeBImmAl)); - SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx, typeof(A32OpCodeBImmAl)); -#endregion - -#region "OpCode Table (AArch64)" - //Integer - SetA64("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc, typeof(AOpCodeAluRs)); - SetA64("x0111010000xxxxx000000xxxxxxxxxx", AInstEmit.Adcs, typeof(AOpCodeAluRs)); - SetA64("x00100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluImm)); - SetA64("00001011<<0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRs)); - SetA64("10001011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRs)); - SetA64("x0001011001xxxxxxxx0xxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRx)); - SetA64("x0001011001xxxxxxxx100xxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRx)); - SetA64("x01100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluImm)); - SetA64("00101011<<0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRs)); - SetA64("10101011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRs)); - SetA64("x0101011001xxxxxxxx0xxxxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRx)); - SetA64("x0101011001xxxxxxxx100xxxxxxxxxx", AInstEmit.Adds, typeof(AOpCodeAluRx)); - SetA64("0xx10000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adr, typeof(AOpCodeAdr)); - SetA64("1xx10000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Adrp, typeof(AOpCodeAdr)); - SetA64("0001001000xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluImm)); - SetA64("100100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluImm)); - SetA64("00001010xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluRs)); - SetA64("10001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.And, typeof(AOpCodeAluRs)); - SetA64("0111001000xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluImm)); - SetA64("111100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluImm)); - SetA64("01101010xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluRs)); - SetA64("11101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluRs)); - SetA64("x0011010110xxxxx001010xxxxxxxxxx", AInstEmit.Asrv, typeof(AOpCodeAluRs)); - SetA64("000101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.B, typeof(AOpCodeBImmAl)); - SetA64("01010100xxxxxxxxxxxxxxxxxxx0xxxx", AInstEmit.B_Cond, typeof(AOpCodeBImmCond)); - SetA64("00110011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bfm, typeof(AOpCodeBfm)); - SetA64("1011001101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bfm, typeof(AOpCodeBfm)); - SetA64("00001010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bic, typeof(AOpCodeAluRs)); - SetA64("10001010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bic, typeof(AOpCodeAluRs)); - SetA64("01101010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bics, typeof(AOpCodeAluRs)); - SetA64("11101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bics, typeof(AOpCodeAluRs)); - SetA64("100101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bl, typeof(AOpCodeBImmAl)); - SetA64("1101011000111111000000xxxxx00000", AInstEmit.Blr, typeof(AOpCodeBReg)); - SetA64("1101011000011111000000xxxxx00000", AInstEmit.Br, typeof(AOpCodeBReg)); - SetA64("11010100001xxxxxxxxxxxxxxxx00000", AInstEmit.Brk, typeof(AOpCodeException)); - SetA64("x0110101xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbnz, typeof(AOpCodeBImmCmp)); - SetA64("x0110100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbz, typeof(AOpCodeBImmCmp)); - SetA64("x0111010010xxxxxxxxx10xxxxx0xxxx", AInstEmit.Ccmn, typeof(AOpCodeCcmpImm)); - SetA64("x0111010010xxxxxxxxx00xxxxx0xxxx", AInstEmit.Ccmn, typeof(AOpCodeCcmpReg)); - SetA64("x1111010010xxxxxxxxx10xxxxx0xxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpImm)); - SetA64("x1111010010xxxxxxxxx00xxxxx0xxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg)); - SetA64("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem)); - SetA64("x101101011000000000101xxxxxxxxxx", AInstEmit.Cls, typeof(AOpCodeAlu)); - SetA64("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu)); - SetA64("00011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010010xxxxxxxxxx", AInstEmit.Crc32w, typeof(AOpCodeAluRs)); - SetA64("10011010110xxxxx010011xxxxxxxxxx", AInstEmit.Crc32x, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010100xxxxxxxxxx", AInstEmit.Crc32cb, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010101xxxxxxxxxx", AInstEmit.Crc32ch, typeof(AOpCodeAluRs)); - SetA64("00011010110xxxxx010110xxxxxxxxxx", AInstEmit.Crc32cw, typeof(AOpCodeAluRs)); - SetA64("10011010110xxxxx010111xxxxxxxxxx", AInstEmit.Crc32cx, typeof(AOpCodeAluRs)); - SetA64("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel)); - SetA64("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel)); - SetA64("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel)); - SetA64("x1011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csneg, typeof(AOpCodeCsel)); - SetA64("11010101000000110011xxxx10111111", AInstEmit.Dmb, typeof(AOpCodeSystem)); - SetA64("11010101000000110011xxxx10011111", AInstEmit.Dsb, typeof(AOpCodeSystem)); - SetA64("01001010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Eon, typeof(AOpCodeAluRs)); - SetA64("11001010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eon, typeof(AOpCodeAluRs)); - SetA64("0101001000xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluImm)); - SetA64("110100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluImm)); - SetA64("01001010xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluRs)); - SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluRs)); - SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Extr, typeof(AOpCodeAluRs)); - SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Extr, typeof(AOpCodeAluRs)); - SetA64("11010101000000110010xxxxxxx11111", AInstEmit.Hint, typeof(AOpCodeSystem)); - SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldar, typeof(AOpCodeMemEx)); - SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxp, typeof(AOpCodeMemEx)); - SetA64("xx001000010xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxr, typeof(AOpCodeMemEx)); - SetA64("<<10100xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeMemPair)); - SetA64("xx111000010xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm)); - SetA64("xx11100101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm)); - SetA64("xx111000011xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemReg)); - SetA64("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeMemLit)); - SetA64("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); - SetA64("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); - SetA64("10111000100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); - SetA64("1011100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); - SetA64("0x1110001x1xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemReg)); - SetA64("10111000101xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemReg)); - SetA64("xx001000010xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ldxr, typeof(AOpCodeMemEx)); - SetA64("1x001000011xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ldxp, typeof(AOpCodeMemEx)); - SetA64("x0011010110xxxxx001000xxxxxxxxxx", AInstEmit.Lslv, typeof(AOpCodeAluRs)); - SetA64("x0011010110xxxxx001001xxxxxxxxxx", AInstEmit.Lsrv, typeof(AOpCodeAluRs)); - SetA64("x0011011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Madd, typeof(AOpCodeMul)); - SetA64("0111001010xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movk, typeof(AOpCodeMov)); - SetA64("111100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movk, typeof(AOpCodeMov)); - SetA64("0001001010xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movn, typeof(AOpCodeMov)); - SetA64("100100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movn, typeof(AOpCodeMov)); - SetA64("0101001010xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movz, typeof(AOpCodeMov)); - SetA64("110100101xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Movz, typeof(AOpCodeMov)); - SetA64("110101010011xxxxxxxxxxxxxxxxxxxx", AInstEmit.Mrs, typeof(AOpCodeSystem)); - SetA64("110101010001xxxxxxxxxxxxxxxxxxxx", AInstEmit.Msr, typeof(AOpCodeSystem)); - SetA64("x0011011000xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Msub, typeof(AOpCodeMul)); - SetA64("11010101000000110010000000011111", AInstEmit.Nop, typeof(AOpCodeSystem)); - SetA64("00101010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Orn, typeof(AOpCodeAluRs)); - SetA64("10101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orn, typeof(AOpCodeAluRs)); - SetA64("0011001000xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm)); - SetA64("101100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm)); - SetA64("00101010xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs)); - SetA64("10101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs)); - SetA64("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm)); - SetA64("11111000100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm)); - SetA64("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit)); - SetA64("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu)); - SetA64("1101011001011111000000xxxxx00000", AInstEmit.Ret, typeof(AOpCodeBReg)); - SetA64("x101101011000000000001xxxxxxxxxx", AInstEmit.Rev16, typeof(AOpCodeAlu)); - SetA64("x101101011000000000010xxxxxxxxxx", AInstEmit.Rev32, typeof(AOpCodeAlu)); - SetA64("1101101011000000000011xxxxxxxxxx", AInstEmit.Rev64, typeof(AOpCodeAlu)); - SetA64("x0011010110xxxxx001011xxxxxxxxxx", AInstEmit.Rorv, typeof(AOpCodeAluRs)); - SetA64("x1011010000xxxxx000000xxxxxxxxxx", AInstEmit.Sbc, typeof(AOpCodeAluRs)); - SetA64("x1111010000xxxxx000000xxxxxxxxxx", AInstEmit.Sbcs, typeof(AOpCodeAluRs)); - SetA64("00010011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Sbfm, typeof(AOpCodeBfm)); - SetA64("1001001101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sbfm, typeof(AOpCodeBfm)); - SetA64("x0011010110xxxxx000011xxxxxxxxxx", AInstEmit.Sdiv, typeof(AOpCodeAluRs)); - SetA64("10011011001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smaddl, typeof(AOpCodeMul)); - SetA64("10011011001xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Smsubl, typeof(AOpCodeMul)); - SetA64("10011011010xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smulh, typeof(AOpCodeMul)); - SetA64("xx001000100xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlr, typeof(AOpCodeMemEx)); - SetA64("1x001000001xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlxp, typeof(AOpCodeMemEx)); - SetA64("xx001000000xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Stlxr, typeof(AOpCodeMemEx)); - SetA64("x010100xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeMemPair)); - SetA64("xx111000000xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemImm)); - SetA64("xx11100100xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemImm)); - SetA64("xx111000001xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeMemReg)); - SetA64("1x001000001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Stxp, typeof(AOpCodeMemEx)); - SetA64("xx001000000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Stxr, typeof(AOpCodeMemEx)); - SetA64("x10100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluImm)); - SetA64("01001011<<0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRs)); - SetA64("11001011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRs)); - SetA64("x1001011001xxxxxxxx0xxxxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRx)); - SetA64("x1001011001xxxxxxxx100xxxxxxxxxx", AInstEmit.Sub, typeof(AOpCodeAluRx)); - SetA64("x11100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluImm)); - SetA64("01101011<<0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRs)); - SetA64("11101011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRs)); - SetA64("x1101011001xxxxxxxx0xxxxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRx)); - SetA64("x1101011001xxxxxxxx100xxxxxxxxxx", AInstEmit.Subs, typeof(AOpCodeAluRx)); - SetA64("11010100000xxxxxxxxxxxxxxxx00001", AInstEmit.Svc, typeof(AOpCodeException)); - SetA64("1101010100001xxxxxxxxxxxxxxxxxxx", AInstEmit.Sys, typeof(AOpCodeSystem)); - SetA64("x0110111xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Tbnz, typeof(AOpCodeBImmTest)); - SetA64("x0110110xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Tbz, typeof(AOpCodeBImmTest)); - SetA64("01010011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Ubfm, typeof(AOpCodeBfm)); - SetA64("1101001101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ubfm, typeof(AOpCodeBfm)); - SetA64("x0011010110xxxxx000010xxxxxxxxxx", AInstEmit.Udiv, typeof(AOpCodeAluRs)); - SetA64("10011011101xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umaddl, typeof(AOpCodeMul)); - SetA64("10011011101xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Umsubl, typeof(AOpCodeMul)); - SetA64("10011011110xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umulh, typeof(AOpCodeMul)); - - //Vector - SetA64("0101111011100000101110xxxxxxxxxx", AInstEmit.Abs_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<100000101110xxxxxxxxxx", AInstEmit.Abs_V, typeof(AOpCodeSimd)); - SetA64("01011110111xxxxx100001xxxxxxxxxx", AInstEmit.Add_S, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Add_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx010000xxxxxxxxxx", AInstEmit.Addhn_V, typeof(AOpCodeSimdReg)); - SetA64("0101111011110001101110xxxxxxxxxx", AInstEmit.Addp_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V, typeof(AOpCodeSimdReg)); - SetA64("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd)); - SetA64("01001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd)); - SetA64("0100111000101000010110xxxxxxxxxx", AInstEmit.Aesd_V, typeof(AOpCodeSimd)); - SetA64("0100111000101000010010xxxxxxxxxx", AInstEmit.Aese_V, typeof(AOpCodeSimd)); - SetA64("0100111000101000011110xxxxxxxxxx", AInstEmit.Aesimc_V, typeof(AOpCodeSimd)); - SetA64("0100111000101000011010xxxxxxxxxx", AInstEmit.Aesmc_V, typeof(AOpCodeSimd)); - SetA64("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg)); - SetA64("0x10111100000xxx<101110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<100000100110xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimd)); - SetA64("01011110111xxxxx001111xxxxxxxxxx", AInstEmit.Cmge_S, typeof(AOpCodeSimdReg)); - SetA64("0111111011100000100010xxxxxxxxxx", AInstEmit.Cmge_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<1xxxxx001111xxxxxxxxxx", AInstEmit.Cmge_V, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<100000100010xxxxxxxxxx", AInstEmit.Cmge_V, typeof(AOpCodeSimd)); - SetA64("01011110111xxxxx001101xxxxxxxxxx", AInstEmit.Cmgt_S, typeof(AOpCodeSimdReg)); - SetA64("0101111011100000100010xxxxxxxxxx", AInstEmit.Cmgt_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<1xxxxx001101xxxxxxxxxx", AInstEmit.Cmgt_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<100000100010xxxxxxxxxx", AInstEmit.Cmgt_V, typeof(AOpCodeSimd)); - SetA64("01111110111xxxxx001101xxxxxxxxxx", AInstEmit.Cmhi_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx001101xxxxxxxxxx", AInstEmit.Cmhi_V, typeof(AOpCodeSimdReg)); - SetA64("01111110111xxxxx001111xxxxxxxxxx", AInstEmit.Cmhs_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx001111xxxxxxxxxx", AInstEmit.Cmhs_V, typeof(AOpCodeSimdReg)); - SetA64("0111111011100000100110xxxxxxxxxx", AInstEmit.Cmle_S, typeof(AOpCodeSimd)); - SetA64("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd)); - SetA64("0101111011100000101010xxxxxxxxxx", AInstEmit.Cmlt_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd)); - SetA64("01011110111xxxxx100011xxxxxxxxxx", AInstEmit.Cmtst_S, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmtst_V, typeof(AOpCodeSimdReg)); - SetA64("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_V, typeof(AOpCodeSimd)); - SetA64("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns)); - SetA64("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns)); - SetA64("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns)); - SetA64("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg)); - SetA64("0>101110000xxxxx00011101<100000111110xxxxxxxxxx", AInstEmit.Fabs_V, typeof(AOpCodeSimd)); - SetA64("000111100x1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, typeof(AOpCodeSimdReg)); - SetA64("011111100x110000110110xxxxxxxxxx", AInstEmit.Faddp_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Faddp_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond)); - SetA64("000111100x1xxxxxxxxx01xxxxx1xxxx", AInstEmit.Fccmpe_S, typeof(AOpCodeSimdFcond)); - SetA64("010111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_S, typeof(AOpCodeSimdReg)); - SetA64("010111101x100000110110xxxxxxxxxx", AInstEmit.Fcmeq_S, typeof(AOpCodeSimd)); - SetA64("0>0011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_V, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<100000110110xxxxxxxxxx", AInstEmit.Fcmeq_V, typeof(AOpCodeSimd)); - SetA64("011111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_S, typeof(AOpCodeSimdReg)); - SetA64("011111101x100000110010xxxxxxxxxx", AInstEmit.Fcmge_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_V, typeof(AOpCodeSimdReg)); - SetA64("0>1011101<100000110010xxxxxxxxxx", AInstEmit.Fcmge_V, typeof(AOpCodeSimd)); - SetA64("011111101x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_S, typeof(AOpCodeSimdReg)); - SetA64("010111101x100000110010xxxxxxxxxx", AInstEmit.Fcmgt_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_V, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<100000110010xxxxxxxxxx", AInstEmit.Fcmgt_V, typeof(AOpCodeSimd)); - SetA64("011111101x100000110110xxxxxxxxxx", AInstEmit.Fcmle_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100000110110xxxxxxxxxx", AInstEmit.Fcmle_V, typeof(AOpCodeSimd)); - SetA64("010111101x100000111010xxxxxxxxxx", AInstEmit.Fcmlt_S, typeof(AOpCodeSimd)); - SetA64("0>0011101<100000111010xxxxxxxxxx", AInstEmit.Fcmlt_V, typeof(AOpCodeSimd)); - SetA64("000111100x1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond)); - SetA64("000111100x10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd)); - SetA64("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt)); - SetA64("0x0011100x100001011110xxxxxxxxxx", AInstEmit.Fcvtl_V, typeof(AOpCodeSimd)); - SetA64("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt)); - SetA64("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd)); - SetA64("010111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtns_S, typeof(AOpCodeSimd)); - SetA64("0>0011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtns_V, typeof(AOpCodeSimd)); - SetA64("011111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_V, typeof(AOpCodeSimd)); - SetA64("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt)); - SetA64("010111101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_S, typeof(AOpCodeSimd)); - SetA64("0>0011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd)); - SetA64("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm)); - SetA64("x00111100x111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt)); - SetA64("x00111100x011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt)); - SetA64("011111101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd)); - SetA64("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm)); - SetA64("000111100x1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg)); - SetA64("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg)); - SetA64("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011100<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011100<1xxxxx110001xxxxxxxxxx", AInstEmit.Fmaxnm_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<1xxxxx110001xxxxxxxxxx", AInstEmit.Fminnm_V, typeof(AOpCodeSimdReg)); - SetA64("010111111<0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); - SetA64("0x0011111<0011101<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmls_V, typeof(AOpCodeSimdReg)); - SetA64("0x0011111<1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg)); - SetA64("0x0011111<1011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd)); - SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg)); - SetA64("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg)); - SetA64("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg)); - SetA64("010111101x100001110110xxxxxxxxxx", AInstEmit.Frecpe_S, typeof(AOpCodeSimd)); - SetA64("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V, typeof(AOpCodeSimd)); - SetA64("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V, typeof(AOpCodeSimd)); - SetA64("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100001100110xxxxxxxxxx", AInstEmit.Frinti_V, typeof(AOpCodeSimd)); - SetA64("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd)); - SetA64("0>0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V, typeof(AOpCodeSimd)); - SetA64("000111100x100100010000xxxxxxxxxx", AInstEmit.Frintn_S, typeof(AOpCodeSimd)); - SetA64("0>0011100<100001100010xxxxxxxxxx", AInstEmit.Frintn_V, typeof(AOpCodeSimd)); - SetA64("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd)); - SetA64("0>0011101<100001100010xxxxxxxxxx", AInstEmit.Frintp_V, typeof(AOpCodeSimd)); - SetA64("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd)); - SetA64("0>1011100<100001100110xxxxxxxxxx", AInstEmit.Frintx_V, typeof(AOpCodeSimd)); - SetA64("011111101x100001110110xxxxxxxxxx", AInstEmit.Frsqrte_S, typeof(AOpCodeSimd)); - SetA64("0>1011101<100001110110xxxxxxxxxx", AInstEmit.Frsqrte_V, typeof(AOpCodeSimd)); - SetA64("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V, typeof(AOpCodeSimdReg)); - SetA64("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd)); - SetA64("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg)); - SetA64("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg)); - SetA64("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns)); - SetA64("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns)); - SetA64("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs)); - SetA64("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs)); - SetA64("0x00110101x00000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs)); - SetA64("0x00110111xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs)); - SetA64("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeSimdMemPair)); - SetA64("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x10xxxxxxxxx11xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); - SetA64("xx111101x1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg)); - SetA64("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit)); - SetA64("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg)); - SetA64("0x101111xxxxxxxx0000x0xxxxxxxxxx", AInstEmit.Mla_Ve, typeof(AOpCodeSimdRegElem)); - SetA64("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V, typeof(AOpCodeSimdReg)); - SetA64("0x101111xxxxxxxx0100x0xxxxxxxxxx", AInstEmit.Mls_Ve, typeof(AOpCodeSimdRegElem)); - SetA64("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); - SetA64("0x00111100000xxx10x001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); - SetA64("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); - SetA64("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); - SetA64("0x001110<<1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg)); - SetA64("0x001111xxxxxxxx1000x0xxxxxxxxxx", AInstEmit.Mul_Ve, typeof(AOpCodeSimdRegElem)); - SetA64("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); - SetA64("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); - SetA64("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); - SetA64("0111111011100000101110xxxxxxxxxx", AInstEmit.Neg_S, typeof(AOpCodeSimd)); - SetA64("0>101110<<100000101110xxxxxxxxxx", AInstEmit.Neg_V, typeof(AOpCodeSimd)); - SetA64("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd)); - SetA64("0x001110111xxxxx000111xxxxxxxxxx", AInstEmit.Orn_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg)); - SetA64("0x00111100000xxx<>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); - SetA64("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd)); - SetA64("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); - SetA64("0x001110<<1xxxxx001001xxxxxxxxxx", AInstEmit.Shsub_V, typeof(AOpCodeSimdReg)); - SetA64("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_V, typeof(AOpCodeSimdShImm)); - SetA64("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Smaxp_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Sminp_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Smlsl_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); - SetA64("01011110xx100000011110xxxxxxxxxx", AInstEmit.Sqabs_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<100000011110xxxxxxxxxx", AInstEmit.Sqabs_V, typeof(AOpCodeSimd)); - SetA64("01011110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_S, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_V, typeof(AOpCodeSimdReg)); - SetA64("01011110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_S, typeof(AOpCodeSimdReg)); - SetA64("01011110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_S, typeof(AOpCodeSimdReg)); - SetA64("0x001110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_V, typeof(AOpCodeSimdReg)); - SetA64("01111110xx100000011110xxxxxxxxxx", AInstEmit.Sqneg_S, typeof(AOpCodeSimd)); - SetA64("0>101110<<100000011110xxxxxxxxxx", AInstEmit.Sqneg_V, typeof(AOpCodeSimd)); - SetA64("01111110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_S, typeof(AOpCodeSimdReg)); - SetA64("01111110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_S, typeof(AOpCodeSimdReg)); - SetA64("0x101110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_V, typeof(AOpCodeSimdReg)); - SetA64("0x00111100>>>xxx100111xxxxxxxxxx", AInstEmit.Sqrshrn_V, typeof(AOpCodeSimdShImm)); - SetA64("01011110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_S, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_V, typeof(AOpCodeSimdReg)); - SetA64("01011110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_S, typeof(AOpCodeSimd)); - SetA64("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd)); - SetA64("01111110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_S, typeof(AOpCodeSimd)); - SetA64("0x101110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_V, typeof(AOpCodeSimd)); - SetA64("0x001110<<1xxxxx000101xxxxxxxxxx", AInstEmit.Srhadd_V, typeof(AOpCodeSimdReg)); - SetA64("0101111101xxxxxx001001xxxxxxxxxx", AInstEmit.Srshr_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx001001xxxxxxxxxx", AInstEmit.Srshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx001001xxxxxxxxxx", AInstEmit.Srshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0101111101xxxxxx001101xxxxxxxxxx", AInstEmit.Srsra_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx001101xxxxxxxxxx", AInstEmit.Srsra_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx001101xxxxxxxxxx", AInstEmit.Srsra_V, typeof(AOpCodeSimdShImm)); - SetA64("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); - SetA64("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); - SetA64("0101111101xxxxxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0101111101xxxxxx000101xxxxxxxxxx", AInstEmit.Ssra_S, typeof(AOpCodeSimdShImm)); - SetA64("0x00111100>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); - SetA64("0100111101xxxxxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); - SetA64("0x001110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Ssubl_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Ssubw_V, typeof(AOpCodeSimdReg)); - SetA64("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); - SetA64("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); - SetA64("0x00110100x00000xxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); - SetA64("0x00110110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); - SetA64("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair)); - SetA64("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x00xxxxxxxxx11xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); - SetA64("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); - SetA64("xx111100x01xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemReg)); - SetA64("01111110111xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Subhn_V, typeof(AOpCodeSimdReg)); - SetA64("01011110xx100000001110xxxxxxxxxx", AInstEmit.Suqadd_S, typeof(AOpCodeSimd)); - SetA64("0>001110<<100000001110xxxxxxxxxx", AInstEmit.Suqadd_V, typeof(AOpCodeSimd)); - SetA64("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl)); - SetA64("0>001110<<0xxxxx001010xxxxxxxxxx", AInstEmit.Trn1_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<0xxxxx011010xxxxxxxxxx", AInstEmit.Trn2_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011111xxxxxxxxxx", AInstEmit.Uaba_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx010100xxxxxxxxxx", AInstEmit.Uabal_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Uabd_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Uabdl_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<100000011010xxxxxxxxxx", AInstEmit.Uadalp_V, typeof(AOpCodeSimd)); - SetA64("0x101110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Uaddl_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<100000001010xxxxxxxxxx", AInstEmit.Uaddlp_V, typeof(AOpCodeSimd)); - SetA64("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd)); - SetA64("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd)); - SetA64("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg)); - SetA64("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt)); - SetA64("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd)); - SetA64("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd)); - SetA64("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx001001xxxxxxxxxx", AInstEmit.Uhsub_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Umax_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Umaxp_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Umin_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Uminp_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Umlal_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Umlsl_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); - SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg)); - SetA64("01111110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_V, typeof(AOpCodeSimdReg)); - SetA64("01111110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_S, typeof(AOpCodeSimdReg)); - SetA64("0>101110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_V, typeof(AOpCodeSimdReg)); - SetA64("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd)); - SetA64("0x101110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_V, typeof(AOpCodeSimd)); - SetA64("0x101110<<1xxxxx000101xxxxxxxxxx", AInstEmit.Urhadd_V, typeof(AOpCodeSimdReg)); - SetA64("0111111101xxxxxx001001xxxxxxxxxx", AInstEmit.Urshr_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx001001xxxxxxxxxx", AInstEmit.Urshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0110111101xxxxxx001001xxxxxxxxxx", AInstEmit.Urshr_V, typeof(AOpCodeSimdShImm)); - SetA64("0111111101xxxxxx001101xxxxxxxxxx", AInstEmit.Ursra_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx001101xxxxxxxxxx", AInstEmit.Ursra_V, typeof(AOpCodeSimdShImm)); - SetA64("0110111101xxxxxx001101xxxxxxxxxx", AInstEmit.Ursra_V, typeof(AOpCodeSimdShImm)); - SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); - SetA64("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm)); - SetA64("0111111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); - SetA64("0110111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); - SetA64("01111110xx100000001110xxxxxxxxxx", AInstEmit.Usqadd_S, typeof(AOpCodeSimd)); - SetA64("0>101110<<100000001110xxxxxxxxxx", AInstEmit.Usqadd_V, typeof(AOpCodeSimd)); - SetA64("0111111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_S, typeof(AOpCodeSimdShImm)); - SetA64("0x10111100>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); - SetA64("0110111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); - SetA64("0x101110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Usubl_V, typeof(AOpCodeSimdReg)); - SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Usubw_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg)); - SetA64("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd)); - SetA64("0>001110<<0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg)); - SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg)); -#endregion - -#region "Generate InstA64FastLookup Table (AArch64)" - var Tmp = new List[FastLookupSize]; - for (int i = 0; i < FastLookupSize; i++) - { - Tmp[i] = new List(); - } - - foreach (var Inst in AllInstA64) - { - int Mask = ToFastLookupIndex(Inst.Mask); - int Value = ToFastLookupIndex(Inst.Value); - - for (int i = 0; i < FastLookupSize; i++) - { - if ((i & Mask) == Value) - { - Tmp[i].Add(Inst); - } - } - } - - for (int i = 0; i < FastLookupSize; i++) - { - InstA64FastLookup[i] = Tmp[i].ToArray(); - } -#endregion - } - - private class InstInfo - { - public int Mask; - public int Value; - - public AInst Inst; - - public InstInfo(int Mask, int Value, AInst Inst) - { - this.Mask = Mask; - this.Value = Value; - this.Inst = Inst; - } - } - - private static List AllInstA32 = new List(); - private static List AllInstA64 = new List(); - - private static int FastLookupSize = 0x1000; - private static InstInfo[][] InstA64FastLookup = new InstInfo[FastLookupSize][]; - - private static void SetA32(string Encoding, AInstInterpreter Interpreter, Type Type) - { - Set(Encoding, new AInst(Interpreter, null, Type), AExecutionMode.AArch32); - } - - private static void SetA64(string Encoding, AInstEmitter Emitter, Type Type) - { - Set(Encoding, new AInst(null, Emitter, Type), AExecutionMode.AArch64); - } - - private static void Set(string Encoding, AInst Inst, AExecutionMode Mode) - { - int Bit = Encoding.Length - 1; - int Value = 0; - int XMask = 0; - int XBits = 0; - - int[] XPos = new int[Encoding.Length]; - - int Blacklisted = 0; - - for (int Index = 0; Index < Encoding.Length; Index++, Bit--) - { - //Note: < and > are used on special encodings. - //The < means that we should never have ALL bits with the '<' set. - //So, when the encoding has <<, it means that 00, 01, and 10 are valid, - //but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on... - //For >, the invalid value is zero. So, for >> 01, 10 and 11 are valid, - //but 00 isn't. - char Chr = Encoding[Index]; - - if (Chr == '1') - { - Value |= 1 << Bit; - } - else if (Chr == 'x') - { - XMask |= 1 << Bit; - } - else if (Chr == '>') - { - XPos[XBits++] = Bit; - } - else if (Chr == '<') - { - XPos[XBits++] = Bit; - - Blacklisted |= 1 << Bit; - } - else if (Chr != '0') - { - throw new ArgumentException(nameof(Encoding)); - } - } - - XMask = ~XMask; - - if (XBits == 0) - { - InsertInst(XMask, Value, Inst, Mode); - - return; - } - - for (int Index = 0; Index < (1 << XBits); Index++) - { - int Mask = 0; - - for (int X = 0; X < XBits; X++) - { - Mask |= ((Index >> X) & 1) << XPos[X]; - } - - if (Mask != Blacklisted) - { - InsertInst(XMask, Value | Mask, Inst, Mode); - } - } - } - - private static void InsertInst( - int XMask, - int Value, - AInst Inst, - AExecutionMode Mode) - { - InstInfo Info = new InstInfo(XMask, Value, Inst); - - if (Mode == AExecutionMode.AArch64) - { - AllInstA64.Add(Info); - } - else - { - AllInstA32.Add(Info); - } - } - - public static AInst GetInstA32(int OpCode) - { - return GetInstFromList(AllInstA32, OpCode); - } - - public static AInst GetInstA64(int OpCode) - { - return GetInstFromList(InstA64FastLookup[ToFastLookupIndex(OpCode)], OpCode); - } - - private static int ToFastLookupIndex(int Value) - { - return ((Value >> 10) & 0x00F) | ((Value >> 18) & 0xFF0); - } - - private static AInst GetInstFromList(IEnumerable InstList, int OpCode) - { - foreach (var Node in InstList) - { - if ((OpCode & Node.Mask) == Node.Value) - { - return Node.Inst; - } - } - - return AInst.Undefined; - } - } -} diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs deleted file mode 100644 index fbf26a4910..0000000000 --- a/ChocolArm64/AOptimizations.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Runtime.Intrinsics.X86; - -public static class AOptimizations -{ - public static bool GenerateCallStack = true; - - private static bool UseAllSseIfAvailable = true; - - private static bool UseSseIfAvailable = true; - private static bool UseSse2IfAvailable = true; - private static bool UseSse41IfAvailable = true; - private static bool UseSse42IfAvailable = true; - - internal static bool UseSse = (UseAllSseIfAvailable && UseSseIfAvailable) && Sse.IsSupported; - internal static bool UseSse2 = (UseAllSseIfAvailable && UseSse2IfAvailable) && Sse2.IsSupported; - internal static bool UseSse41 = (UseAllSseIfAvailable && UseSse41IfAvailable) && Sse41.IsSupported; - internal static bool UseSse42 = (UseAllSseIfAvailable && UseSse42IfAvailable) && Sse42.IsSupported; -} \ No newline at end of file diff --git a/ChocolArm64/AThread.cs b/ChocolArm64/AThread.cs deleted file mode 100644 index 7b8360f8fe..0000000000 --- a/ChocolArm64/AThread.cs +++ /dev/null @@ -1,67 +0,0 @@ -using ChocolArm64.Memory; -using ChocolArm64.State; -using System; -using System.Threading; - -namespace ChocolArm64 -{ - public class AThread - { - public AThreadState ThreadState { get; private set; } - public AMemory Memory { get; private set; } - - private long EntryPoint; - - private ATranslator Translator; - - private Thread Work; - - public event EventHandler WorkFinished; - - private int IsExecuting; - - public AThread(ATranslator Translator, AMemory Memory, long EntryPoint) - { - this.Translator = Translator; - this.Memory = Memory; - this.EntryPoint = EntryPoint; - - ThreadState = new AThreadState(); - - ThreadState.ExecutionMode = AExecutionMode.AArch64; - - ThreadState.Running = true; - } - - public bool Execute() - { - if (Interlocked.Exchange(ref IsExecuting, 1) == 1) - { - return false; - } - - Work = new Thread(delegate() - { - Translator.ExecuteSubroutine(this, EntryPoint); - - Memory.RemoveMonitor(ThreadState); - - WorkFinished?.Invoke(this, EventArgs.Empty); - }); - - Work.Start(); - - return true; - } - - public void StopExecution() - { - ThreadState.Running = false; - } - - public bool IsCurrentThread() - { - return Thread.CurrentThread == Work; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/ATranslatedSub.cs b/ChocolArm64/ATranslatedSub.cs deleted file mode 100644 index 9dbc378ec0..0000000000 --- a/ChocolArm64/ATranslatedSub.cs +++ /dev/null @@ -1,150 +0,0 @@ -using ChocolArm64.Memory; -using ChocolArm64.State; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64 -{ - class ATranslatedSub - { - private delegate long AA64Subroutine(AThreadState Register, AMemory Memory); - - private AA64Subroutine ExecDelegate; - - public static int StateArgIdx { get; private set; } - public static int MemoryArgIdx { get; private set; } - - public static Type[] FixedArgTypes { get; private set; } - - public DynamicMethod Method { get; private set; } - - public ReadOnlyCollection Params { get; private set; } - - private HashSet Callers; - - private ATranslatedSubType Type; - - private int CallCount; - - private bool NeedsReJit; - - private int MinCallCountForReJit = 250; - - public ATranslatedSub(DynamicMethod Method, List Params) - { - if (Method == null) - { - throw new ArgumentNullException(nameof(Method)); - } - - if (Params == null) - { - throw new ArgumentNullException(nameof(Params)); - } - - this.Method = Method; - this.Params = Params.AsReadOnly(); - - Callers = new HashSet(); - - PrepareDelegate(); - } - - static ATranslatedSub() - { - MethodInfo MthdInfo = typeof(AA64Subroutine).GetMethod("Invoke"); - - ParameterInfo[] Params = MthdInfo.GetParameters(); - - FixedArgTypes = new Type[Params.Length]; - - for (int Index = 0; Index < Params.Length; Index++) - { - Type ParamType = Params[Index].ParameterType; - - FixedArgTypes[Index] = ParamType; - - if (ParamType == typeof(AThreadState)) - { - StateArgIdx = Index; - } - else if (ParamType == typeof(AMemory)) - { - MemoryArgIdx = Index; - } - } - } - - private void PrepareDelegate() - { - string Name = $"{Method.Name}_Dispatch"; - - DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes); - - ILGenerator Generator = Mthd.GetILGenerator(); - - Generator.EmitLdargSeq(FixedArgTypes.Length); - - foreach (ARegister Reg in Params) - { - Generator.EmitLdarg(StateArgIdx); - - Generator.Emit(OpCodes.Ldfld, Reg.GetField()); - } - - Generator.Emit(OpCodes.Call, Method); - Generator.Emit(OpCodes.Ret); - - ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine)); - } - - public bool ShouldReJit() - { - if (NeedsReJit && CallCount < MinCallCountForReJit) - { - CallCount++; - - return false; - } - - return NeedsReJit; - } - - public long Execute(AThreadState ThreadState, AMemory Memory) - { - return ExecDelegate(ThreadState, Memory); - } - - public void AddCaller(long Position) - { - lock (Callers) - { - Callers.Add(Position); - } - } - - public long[] GetCallerPositions() - { - lock (Callers) - { - return Callers.ToArray(); - } - } - - public void SetType(ATranslatedSubType Type) - { - this.Type = Type; - - if (Type == ATranslatedSubType.SubTier0) - { - NeedsReJit = true; - } - } - - public void MarkForReJit() => NeedsReJit = true; - } -} \ No newline at end of file diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs deleted file mode 100644 index 2d9fcb1415..0000000000 --- a/ChocolArm64/ATranslator.cs +++ /dev/null @@ -1,206 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Events; -using ChocolArm64.Instruction; -using ChocolArm64.Memory; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Reflection.Emit; - -namespace ChocolArm64 -{ - public class ATranslator - { - private ConcurrentDictionary CachedSubs; - - private ConcurrentDictionary SymbolTable; - - public event EventHandler CpuTrace; - - public bool EnableCpuTrace { get; set; } - - public ATranslator(IReadOnlyDictionary SymbolTable = null) - { - CachedSubs = new ConcurrentDictionary(); - - if (SymbolTable != null) - { - this.SymbolTable = new ConcurrentDictionary(SymbolTable); - } - else - { - this.SymbolTable = new ConcurrentDictionary(); - } - } - - internal void ExecuteSubroutine(AThread Thread, long Position) - { - //TODO: Both the execute A32/A64 methods should be merged on the future, - //when both ISAs are implemented with the interpreter and JIT. - //As of now, A32 only has a interpreter and A64 a JIT. - AThreadState State = Thread.ThreadState; - AMemory Memory = Thread.Memory; - - if (State.ExecutionMode == AExecutionMode.AArch32) - { - ExecuteSubroutineA32(State, Memory); - } - else - { - ExecuteSubroutineA64(State, Memory, Position); - } - } - - private void ExecuteSubroutineA32(AThreadState State, AMemory Memory) - { - do - { - AOpCode OpCode = ADecoder.DecodeOpCode(State, Memory, State.R15); - - OpCode.Interpreter(State, Memory, OpCode); - } - while (State.R15 != 0 && State.Running); - } - - private void ExecuteSubroutineA64(AThreadState State, AMemory Memory, long Position) - { - do - { - if (EnableCpuTrace) - { - if (!SymbolTable.TryGetValue(Position, out string SubName)) - { - SubName = string.Empty; - } - - CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position, SubName)); - } - - if (!CachedSubs.TryGetValue(Position, out ATranslatedSub Sub)) - { - Sub = TranslateTier0(State, Memory, Position); - } - - if (Sub.ShouldReJit()) - { - TranslateTier1(State, Memory, Position); - } - - Position = Sub.Execute(State, Memory); - } - while (Position != 0 && State.Running); - } - - internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) - { - if (OpCode.Emitter != AInstEmit.Bl) - { - Sub = null; - - return false; - } - - return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub); - } - - internal bool TryGetCachedSub(long Position, out ATranslatedSub Sub) - { - return CachedSubs.TryGetValue(Position, out Sub); - } - - internal bool HasCachedSub(long Position) - { - return CachedSubs.ContainsKey(Position); - } - - private ATranslatedSub TranslateTier0(AThreadState State, AMemory Memory, long Position) - { - ABlock Block = ADecoder.DecodeBasicBlock(State, this, Memory, Position); - - ABlock[] Graph = new ABlock[] { Block }; - - string SubName = GetSubName(Position); - - AILEmitterCtx Context = new AILEmitterCtx(this, Graph, Block, SubName); - - do - { - Context.EmitOpCode(); - } - while (Context.AdvanceOpCode()); - - ATranslatedSub Subroutine = Context.GetSubroutine(); - - Subroutine.SetType(ATranslatedSubType.SubTier0); - - CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine); - - AOpCode LastOp = Block.GetLastOp(); - - return Subroutine; - } - - private void TranslateTier1(AThreadState State, AMemory Memory, long Position) - { - (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(State, this, Memory, Position); - - string SubName = GetSubName(Position); - - PropagateName(Cfg.Graph, SubName); - - AILEmitterCtx Context = new AILEmitterCtx(this, Cfg.Graph, Cfg.Root, SubName); - - if (Context.CurrBlock.Position != Position) - { - Context.Emit(OpCodes.Br, Context.GetLabel(Position)); - } - - do - { - Context.EmitOpCode(); - } - while (Context.AdvanceOpCode()); - - //Mark all methods that calls this method for ReJiting, - //since we can now call it directly which is faster. - if (CachedSubs.TryGetValue(Position, out ATranslatedSub OldSub)) - { - foreach (long CallerPos in OldSub.GetCallerPositions()) - { - if (CachedSubs.TryGetValue(Position, out ATranslatedSub CallerSub)) - { - CallerSub.MarkForReJit(); - } - } - } - - ATranslatedSub Subroutine = Context.GetSubroutine(); - - Subroutine.SetType(ATranslatedSubType.SubTier1); - - CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine); - } - - private string GetSubName(long Position) - { - return SymbolTable.GetOrAdd(Position, $"Sub{Position:x16}"); - } - - private void PropagateName(ABlock[] Graph, string Name) - { - foreach (ABlock Block in Graph) - { - AOpCode LastOp = Block.GetLastOp(); - - if (LastOp != null && - (LastOp.Emitter == AInstEmit.Bl || - LastOp.Emitter == AInstEmit.Blr)) - { - SymbolTable.TryAdd(LastOp.Position + 4, Name); - } - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/BitUtils.cs b/ChocolArm64/BitUtils.cs new file mode 100644 index 0000000000..881ee24842 --- /dev/null +++ b/ChocolArm64/BitUtils.cs @@ -0,0 +1,49 @@ +namespace ChocolArm64 +{ + static class BitUtils + { + public static int HighestBitSet32(int value) + { + for (int bit = 31; bit >= 0; bit--) + { + if (((value >> bit) & 1) != 0) + { + return bit; + } + } + + return -1; + } + + private static readonly sbyte[] HbsNibbleTbl = { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; + + public static int HighestBitSetNibble(int value) => HbsNibbleTbl[value & 0b1111]; + + public static long Replicate(long bits, int size) + { + long output = 0; + + for (int bit = 0; bit < 64; bit += size) + { + output |= bits << bit; + } + + return output; + } + + public static long FillWithOnes(int bits) + { + return bits == 64 ? -1L : (1L << bits) - 1; + } + + public static long RotateRight(long bits, int shift, int size) + { + return (long)RotateRight((ulong)bits, shift, size); + } + + public static ulong RotateRight(ulong bits, int shift, int size) + { + return (bits >> shift) | (bits << (size - shift)); + } + } +} diff --git a/ChocolArm64/CpuThread.cs b/ChocolArm64/CpuThread.cs new file mode 100644 index 0000000000..11f41236e3 --- /dev/null +++ b/ChocolArm64/CpuThread.cs @@ -0,0 +1,69 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using System; +using System.Threading; + +namespace ChocolArm64 +{ + public class CpuThread + { + public CpuThreadState ThreadState { get; private set; } + public MemoryManager Memory { get; private set; } + + private Translator _translator; + + public Thread Work; + + public event EventHandler WorkFinished; + + private int _isExecuting; + + public CpuThread(Translator translator, MemoryManager memory, long entryPoint) + { + _translator = translator; + Memory = memory; + + ThreadState = new CpuThreadState(); + + ThreadState.ExecutionMode = ExecutionMode.AArch64; + + ThreadState.Running = true; + + Work = new Thread(delegate() + { + translator.ExecuteSubroutine(this, entryPoint); + + memory.RemoveMonitor(ThreadState.Core); + + WorkFinished?.Invoke(this, EventArgs.Empty); + }); + } + + public bool Execute() + { + if (Interlocked.Exchange(ref _isExecuting, 1) == 1) + { + return false; + } + + Work.Start(); + + return true; + } + + public void StopExecution() + { + ThreadState.Running = false; + } + + public void RequestInterrupt() + { + ThreadState.RequestInterrupt(); + } + + public bool IsCurrentThread() + { + return Thread.CurrentThread == Work; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoder/ABlock.cs b/ChocolArm64/Decoder/ABlock.cs deleted file mode 100644 index 7a0fc60773..0000000000 --- a/ChocolArm64/Decoder/ABlock.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; - -namespace ChocolArm64.Decoder -{ - class ABlock - { - public long Position { get; set; } - public long EndPosition { get; set; } - - public ABlock Next { get; set; } - public ABlock Branch { get; set; } - - public List OpCodes { get; private set; } - - public ABlock() - { - OpCodes = new List(); - } - - public ABlock(long Position) : this() - { - this.Position = Position; - } - - public AOpCode GetLastOp() - { - if (OpCodes.Count > 0) - { - return OpCodes[OpCodes.Count - 1]; - } - - return null; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/ACond.cs b/ChocolArm64/Decoder/ACond.cs deleted file mode 100644 index f2da8bd29f..0000000000 --- a/ChocolArm64/Decoder/ACond.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace ChocolArm64.Decoder -{ - enum ACond - { - Eq = 0, - Ne = 1, - Ge_Un = 2, - Lt_Un = 3, - Mi = 4, - Pl = 5, - Vs = 6, - Vc = 7, - Gt_Un = 8, - Le_Un = 9, - Ge = 10, - Lt = 11, - Gt = 12, - Le = 13, - Al = 14, - Nv = 15 - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/ADecoder.cs b/ChocolArm64/Decoder/ADecoder.cs deleted file mode 100644 index b154a54cd2..0000000000 --- a/ChocolArm64/Decoder/ADecoder.cs +++ /dev/null @@ -1,243 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.Memory; -using ChocolArm64.State; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Reflection.Emit; - -namespace ChocolArm64.Decoder -{ - static class ADecoder - { - private delegate object OpActivator(AInst Inst, long Position, int OpCode); - - private static ConcurrentDictionary OpActivators; - - static ADecoder() - { - OpActivators = new ConcurrentDictionary(); - } - - public static ABlock DecodeBasicBlock( - AThreadState State, - ATranslator Translator, - AMemory Memory, - long Start) - { - ABlock Block = new ABlock(Start); - - FillBlock(State, Memory, Block); - - return Block; - } - - public static (ABlock[] Graph, ABlock Root) DecodeSubroutine( - AThreadState State, - ATranslator Translator, - AMemory Memory, - long Start) - { - Dictionary Visited = new Dictionary(); - Dictionary VisitedEnd = new Dictionary(); - - Queue Blocks = new Queue(); - - ABlock Enqueue(long Position) - { - if (!Visited.TryGetValue(Position, out ABlock Output)) - { - Output = new ABlock(Position); - - Blocks.Enqueue(Output); - - Visited.Add(Position, Output); - } - - return Output; - } - - ABlock Root = Enqueue(Start); - - while (Blocks.Count > 0) - { - ABlock Current = Blocks.Dequeue(); - - FillBlock(State, Memory, Current); - - //Set child blocks. "Branch" is the block the branch instruction - //points to (when taken), "Next" is the block at the next address, - //executed when the branch is not taken. For Unconditional Branches - //(except BL/BLR that are sub calls) or end of executable, Next is null. - if (Current.OpCodes.Count > 0) - { - bool HasCachedSub = false; - - AOpCode LastOp = Current.GetLastOp(); - - if (LastOp is AOpCodeBImm Op) - { - if (Op.Emitter == AInstEmit.Bl) - { - HasCachedSub = Translator.HasCachedSub(Op.Imm); - } - else - { - Current.Branch = Enqueue(Op.Imm); - } - } - - if (!((LastOp is AOpCodeBImmAl) || - (LastOp is AOpCodeBReg)) || HasCachedSub) - { - Current.Next = Enqueue(Current.EndPosition); - } - } - - //If we have on the graph two blocks with the same end position, - //then we need to split the bigger block and have two small blocks, - //the end position of the bigger "Current" block should then be == to - //the position of the "Smaller" block. - while (VisitedEnd.TryGetValue(Current.EndPosition, out ABlock Smaller)) - { - if (Current.Position > Smaller.Position) - { - ABlock Temp = Smaller; - - Smaller = Current; - Current = Temp; - } - - Current.EndPosition = Smaller.Position; - Current.Next = Smaller; - Current.Branch = null; - - Current.OpCodes.RemoveRange( - Current.OpCodes.Count - Smaller.OpCodes.Count, - Smaller.OpCodes.Count); - - VisitedEnd[Smaller.EndPosition] = Smaller; - } - - VisitedEnd.Add(Current.EndPosition, Current); - } - - //Make and sort Graph blocks array by position. - ABlock[] Graph = new ABlock[Visited.Count]; - - while (Visited.Count > 0) - { - ulong FirstPos = ulong.MaxValue; - - foreach (ABlock Block in Visited.Values) - { - if (FirstPos > (ulong)Block.Position) - FirstPos = (ulong)Block.Position; - } - - ABlock Current = Visited[(long)FirstPos]; - - do - { - Graph[Graph.Length - Visited.Count] = Current; - - Visited.Remove(Current.Position); - - Current = Current.Next; - } - while (Current != null); - } - - return (Graph, Root); - } - - private static void FillBlock(AThreadState State, AMemory Memory, ABlock Block) - { - long Position = Block.Position; - - AOpCode OpCode; - - do - { - //TODO: This needs to be changed to support both AArch32 and AArch64, - //once JIT support is introduced on AArch32 aswell. - OpCode = DecodeOpCode(State, Memory, Position); - - Block.OpCodes.Add(OpCode); - - Position += 4; - } - while (!(IsBranch(OpCode) || IsException(OpCode))); - - Block.EndPosition = Position; - } - - private static bool IsBranch(AOpCode OpCode) - { - return OpCode is AOpCodeBImm || - OpCode is AOpCodeBReg; - } - - private static bool IsException(AOpCode OpCode) - { - return OpCode.Emitter == AInstEmit.Brk || - OpCode.Emitter == AInstEmit.Svc || - OpCode.Emitter == AInstEmit.Und; - } - - public static AOpCode DecodeOpCode(AThreadState State, AMemory Memory, long Position) - { - int OpCode = Memory.ReadInt32(Position); - - AInst Inst; - - if (State.ExecutionMode == AExecutionMode.AArch64) - { - Inst = AOpCodeTable.GetInstA64(OpCode); - } - else - { - //TODO: Thumb support. - Inst = AOpCodeTable.GetInstA32(OpCode); - } - - AOpCode DecodedOpCode = new AOpCode(AInst.Undefined, Position, OpCode); - - if (Inst.Type != null) - { - DecodedOpCode = MakeOpCode(Inst.Type, Inst, Position, OpCode); - } - - return DecodedOpCode; - } - - private static AOpCode MakeOpCode(Type Type, AInst Inst, long Position, int OpCode) - { - if (Type == null) - { - throw new ArgumentNullException(nameof(Type)); - } - - OpActivator CreateInstance = OpActivators.GetOrAdd(Type, CacheOpActivator); - - return (AOpCode)CreateInstance(Inst, Position, OpCode); - } - - private static OpActivator CacheOpActivator(Type Type) - { - Type[] ArgTypes = new Type[] { typeof(AInst), typeof(long), typeof(int) }; - - DynamicMethod Mthd = new DynamicMethod($"Make{Type.Name}", Type, ArgTypes); - - ILGenerator Generator = Mthd.GetILGenerator(); - - Generator.Emit(OpCodes.Ldarg_0); - Generator.Emit(OpCodes.Ldarg_1); - Generator.Emit(OpCodes.Ldarg_2); - Generator.Emit(OpCodes.Newobj, Type.GetConstructor(ArgTypes)); - Generator.Emit(OpCodes.Ret); - - return (OpActivator)Mthd.CreateDelegate(typeof(OpActivator)); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/ADecoderHelper.cs b/ChocolArm64/Decoder/ADecoderHelper.cs deleted file mode 100644 index a2179f49e3..0000000000 --- a/ChocolArm64/Decoder/ADecoderHelper.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; - -namespace ChocolArm64.Decoder -{ - static class ADecoderHelper - { - public struct BitMask - { - public long WMask; - public long TMask; - public int Pos; - public int Shift; - public bool IsUndefined; - - public static BitMask Invalid => new BitMask { IsUndefined = true }; - } - - public static BitMask DecodeBitMask(int OpCode, bool Immediate) - { - int ImmS = (OpCode >> 10) & 0x3f; - int ImmR = (OpCode >> 16) & 0x3f; - - int N = (OpCode >> 22) & 1; - int SF = (OpCode >> 31) & 1; - - int Length = ABitUtils.HighestBitSet32((~ImmS & 0x3f) | (N << 6)); - - if (Length < 1 || (SF == 0 && N != 0)) - { - return BitMask.Invalid; - } - - int Size = 1 << Length; - - int Levels = Size - 1; - - int S = ImmS & Levels; - int R = ImmR & Levels; - - if (Immediate && S == Levels) - { - return BitMask.Invalid; - } - - long WMask = ABitUtils.FillWithOnes(S + 1); - long TMask = ABitUtils.FillWithOnes(((S - R) & Levels) + 1); - - if (R > 0) - { - WMask = ABitUtils.RotateRight(WMask, R, Size); - WMask &= ABitUtils.FillWithOnes(Size); - } - - return new BitMask() - { - WMask = ABitUtils.Replicate(WMask, Size), - TMask = ABitUtils.Replicate(TMask, Size), - - Pos = ImmS, - Shift = ImmR - }; - } - - public static long DecodeImm8Float(long Imm, int Size) - { - int E = 0, F = 0; - - switch (Size) - { - case 0: E = 8; F = 23; break; - case 1: E = 11; F = 52; break; - - default: throw new ArgumentOutOfRangeException(nameof(Size)); - } - - long Value = (Imm & 0x3f) << F - 4; - - long EBit = (Imm >> 6) & 1; - long SBit = (Imm >> 7) & 1; - - if (EBit != 0) - { - Value |= (1L << E - 3) - 1 << F + 2; - } - - Value |= (EBit ^ 1) << F + E - 1; - Value |= SBit << F + E; - - return Value; - } - - public static long DecodeImm26_2(int OpCode) - { - return ((long)OpCode << 38) >> 36; - } - - public static long DecodeImmS19_2(int OpCode) - { - return (((long)OpCode << 40) >> 43) & ~3; - } - - public static long DecodeImmS14_2(int OpCode) - { - return (((long)OpCode << 45) >> 48) & ~3; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCode.cs b/ChocolArm64/Decoder/AOpCode.cs deleted file mode 100644 index bdc8f13aa0..0000000000 --- a/ChocolArm64/Decoder/AOpCode.cs +++ /dev/null @@ -1,40 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; -using System; - -namespace ChocolArm64.Decoder -{ - class AOpCode : IAOpCode - { - public long Position { get; private set; } - public int RawOpCode { get; private set; } - - public AInstEmitter Emitter { get; protected set; } - public AInstInterpreter Interpreter { get; protected set; } - public ARegisterSize RegisterSize { get; protected set; } - - public AOpCode(AInst Inst, long Position, int OpCode) - { - this.Position = Position; - this.RawOpCode = OpCode; - - RegisterSize = ARegisterSize.Int64; - - Emitter = Inst.Emitter; - Interpreter = Inst.Interpreter; - } - - public int GetBitsCount() - { - switch (RegisterSize) - { - case ARegisterSize.Int32: return 32; - case ARegisterSize.Int64: return 64; - case ARegisterSize.SIMD64: return 64; - case ARegisterSize.SIMD128: return 128; - } - - throw new InvalidOperationException(); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeAdr.cs b/ChocolArm64/Decoder/AOpCodeAdr.cs deleted file mode 100644 index 3396281f45..0000000000 --- a/ChocolArm64/Decoder/AOpCodeAdr.cs +++ /dev/null @@ -1,18 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAdr : AOpCode - { - public int Rd { get; private set; } - public long Imm { get; private set; } - - public AOpCodeAdr(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rd = OpCode & 0x1f; - - Imm = ADecoderHelper.DecodeImmS19_2(OpCode); - Imm |= ((long)OpCode >> 29) & 3; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeAlu.cs b/ChocolArm64/Decoder/AOpCodeAlu.cs deleted file mode 100644 index e1a44f04b4..0000000000 --- a/ChocolArm64/Decoder/AOpCodeAlu.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAlu : AOpCode, IAOpCodeAlu - { - public int Rd { get; protected set; } - public int Rn { get; private set; } - - public ADataOp DataOp { get; private set; } - - public AOpCodeAlu(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rd = (OpCode >> 0) & 0x1f; - Rn = (OpCode >> 5) & 0x1f; - DataOp = (ADataOp)((OpCode >> 24) & 0x3); - - RegisterSize = (OpCode >> 31) != 0 - ? ARegisterSize.Int64 - : ARegisterSize.Int32; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeAluImm.cs b/ChocolArm64/Decoder/AOpCodeAluImm.cs deleted file mode 100644 index e913475ad2..0000000000 --- a/ChocolArm64/Decoder/AOpCodeAluImm.cs +++ /dev/null @@ -1,39 +0,0 @@ -using ChocolArm64.Instruction; -using System; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAluImm : AOpCodeAlu, IAOpCodeAluImm - { - public long Imm { get; private set; } - - public AOpCodeAluImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - if (DataOp == ADataOp.Arithmetic) - { - Imm = (OpCode >> 10) & 0xfff; - - int Shift = (OpCode >> 22) & 3; - - Imm <<= Shift * 12; - } - else if (DataOp == ADataOp.Logical) - { - var BM = ADecoderHelper.DecodeBitMask(OpCode, true); - - if (BM.IsUndefined) - { - Emitter = AInstEmit.Und; - - return; - } - - Imm = BM.WMask; - } - else - { - throw new ArgumentException(nameof(OpCode)); - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeAluRs.cs b/ChocolArm64/Decoder/AOpCodeAluRs.cs deleted file mode 100644 index 9c215be383..0000000000 --- a/ChocolArm64/Decoder/AOpCodeAluRs.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAluRs : AOpCodeAlu, IAOpCodeAluRs - { - public int Shift { get; private set; } - public int Rm { get; private set; } - - public AShiftType ShiftType { get; private set; } - - public AOpCodeAluRs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Shift = (OpCode >> 10) & 0x3f; - - if (Shift >= GetBitsCount()) - { - Emitter = AInstEmit.Und; - - return; - } - - this.Shift = Shift; - - Rm = (OpCode >> 16) & 0x1f; - ShiftType = (AShiftType)((OpCode >> 22) & 0x3); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeAluRx.cs b/ChocolArm64/Decoder/AOpCodeAluRx.cs deleted file mode 100644 index 7dd72a6842..0000000000 --- a/ChocolArm64/Decoder/AOpCodeAluRx.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeAluRx : AOpCodeAlu, IAOpCodeAluRx - { - public int Shift { get; private set; } - public int Rm { get; private set; } - - public AIntType IntType { get; private set; } - - public AOpCodeAluRx(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Shift = (OpCode >> 10) & 0x7; - IntType = (AIntType)((OpCode >> 13) & 0x7); - Rm = (OpCode >> 16) & 0x1f; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeBImm.cs b/ChocolArm64/Decoder/AOpCodeBImm.cs deleted file mode 100644 index 6519d281dc..0000000000 --- a/ChocolArm64/Decoder/AOpCodeBImm.cs +++ /dev/null @@ -1,11 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImm : AOpCode - { - public long Imm { get; protected set; } - - public AOpCodeBImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeBImmAl.cs b/ChocolArm64/Decoder/AOpCodeBImmAl.cs deleted file mode 100644 index a4ff686d6d..0000000000 --- a/ChocolArm64/Decoder/AOpCodeBImmAl.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImmAl : AOpCodeBImm - { - public AOpCodeBImmAl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Imm = Position + ADecoderHelper.DecodeImm26_2(OpCode); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeBImmCmp.cs b/ChocolArm64/Decoder/AOpCodeBImmCmp.cs deleted file mode 100644 index 0f16b73e0e..0000000000 --- a/ChocolArm64/Decoder/AOpCodeBImmCmp.cs +++ /dev/null @@ -1,21 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImmCmp : AOpCodeBImm - { - public int Rt { get; private set; } - - public AOpCodeBImmCmp(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt = OpCode & 0x1f; - - Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); - - RegisterSize = (OpCode >> 31) != 0 - ? ARegisterSize.Int64 - : ARegisterSize.Int32; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeBImmCond.cs b/ChocolArm64/Decoder/AOpCodeBImmCond.cs deleted file mode 100644 index 1310feb8d3..0000000000 --- a/ChocolArm64/Decoder/AOpCodeBImmCond.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImmCond : AOpCodeBImm, IAOpCodeCond - { - public ACond Cond { get; private set; } - - public AOpCodeBImmCond(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int O0 = (OpCode >> 4) & 1; - - if (O0 != 0) - { - Emitter = AInstEmit.Und; - - return; - } - - Cond = (ACond)(OpCode & 0xf); - - Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeBImmTest.cs b/ChocolArm64/Decoder/AOpCodeBImmTest.cs deleted file mode 100644 index 73e57b7ab9..0000000000 --- a/ChocolArm64/Decoder/AOpCodeBImmTest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBImmTest : AOpCodeBImm - { - public int Rt { get; private set; } - public int Pos { get; private set; } - - public AOpCodeBImmTest(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt = OpCode & 0x1f; - - Imm = Position + ADecoderHelper.DecodeImmS14_2(OpCode); - - Pos = (OpCode >> 19) & 0x1f; - Pos |= (OpCode >> 26) & 0x20; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeBReg.cs b/ChocolArm64/Decoder/AOpCodeBReg.cs deleted file mode 100644 index c9c600af5b..0000000000 --- a/ChocolArm64/Decoder/AOpCodeBReg.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBReg : AOpCode - { - public int Rn { get; private set; } - - public AOpCodeBReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Op4 = (OpCode >> 0) & 0x1f; - int Op2 = (OpCode >> 16) & 0x1f; - - if (Op2 != 0b11111 || Op4 != 0b00000) - { - Emitter = AInstEmit.Und; - - return; - } - - Rn = (OpCode >> 5) & 0x1f; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeBfm.cs b/ChocolArm64/Decoder/AOpCodeBfm.cs deleted file mode 100644 index 6498d8ec69..0000000000 --- a/ChocolArm64/Decoder/AOpCodeBfm.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeBfm : AOpCodeAlu - { - public long WMask { get; private set; } - public long TMask { get; private set; } - public int Pos { get; private set; } - public int Shift { get; private set; } - - public AOpCodeBfm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - var BM = ADecoderHelper.DecodeBitMask(OpCode, false); - - if (BM.IsUndefined) - { - Emitter = AInstEmit.Und; - - return; - } - - WMask = BM.WMask; - TMask = BM.TMask; - Pos = BM.Pos; - Shift = BM.Shift; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeCcmp.cs b/ChocolArm64/Decoder/AOpCodeCcmp.cs deleted file mode 100644 index d0c7f779c8..0000000000 --- a/ChocolArm64/Decoder/AOpCodeCcmp.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeCcmp : AOpCodeAlu, IAOpCodeCond - { - public int NZCV { get; private set; } - protected int RmImm; - - public ACond Cond { get; private set; } - - public AOpCodeCcmp(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int O3 = (OpCode >> 4) & 1; - - if (O3 != 0) - { - Emitter = AInstEmit.Und; - - return; - } - - NZCV = (OpCode >> 0) & 0xf; - Cond = (ACond)((OpCode >> 12) & 0xf); - RmImm = (OpCode >> 16) & 0x1f; - - Rd = AThreadState.ZRIndex; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeCcmpImm.cs b/ChocolArm64/Decoder/AOpCodeCcmpImm.cs deleted file mode 100644 index 803eefc249..0000000000 --- a/ChocolArm64/Decoder/AOpCodeCcmpImm.cs +++ /dev/null @@ -1,11 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeCcmpImm : AOpCodeCcmp, IAOpCodeAluImm - { - public long Imm => RmImm; - - public AOpCodeCcmpImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeCcmpReg.cs b/ChocolArm64/Decoder/AOpCodeCcmpReg.cs deleted file mode 100644 index c364ae68b4..0000000000 --- a/ChocolArm64/Decoder/AOpCodeCcmpReg.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeCcmpReg : AOpCodeCcmp, IAOpCodeAluRs - { - public int Rm => RmImm; - - public int Shift => 0; - - public AShiftType ShiftType => AShiftType.Lsl; - - public AOpCodeCcmpReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeCsel.cs b/ChocolArm64/Decoder/AOpCodeCsel.cs deleted file mode 100644 index cdef3e745c..0000000000 --- a/ChocolArm64/Decoder/AOpCodeCsel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeCsel : AOpCodeAlu, IAOpCodeCond - { - public int Rm { get; private set; } - - public ACond Cond { get; private set; } - - public AOpCodeCsel(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rm = (OpCode >> 16) & 0x1f; - Cond = (ACond)((OpCode >> 12) & 0xf); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeException.cs b/ChocolArm64/Decoder/AOpCodeException.cs deleted file mode 100644 index 4579c1a7b9..0000000000 --- a/ChocolArm64/Decoder/AOpCodeException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeException : AOpCode - { - public int Id { get; private set; } - - public AOpCodeException(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Id = (OpCode >> 5) & 0xffff; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeMem.cs b/ChocolArm64/Decoder/AOpCodeMem.cs deleted file mode 100644 index be5367cf61..0000000000 --- a/ChocolArm64/Decoder/AOpCodeMem.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMem : AOpCode - { - public int Rt { get; protected set; } - public int Rn { get; protected set; } - public int Size { get; protected set; } - public bool Extend64 { get; protected set; } - - public AOpCodeMem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt = (OpCode >> 0) & 0x1f; - Rn = (OpCode >> 5) & 0x1f; - Size = (OpCode >> 30) & 0x3; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeMemEx.cs b/ChocolArm64/Decoder/AOpCodeMemEx.cs deleted file mode 100644 index 3a28cfd73a..0000000000 --- a/ChocolArm64/Decoder/AOpCodeMemEx.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMemEx : AOpCodeMem - { - public int Rt2 { get; private set; } - public int Rs { get; private set; } - - public AOpCodeMemEx(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt2 = (OpCode >> 10) & 0x1f; - Rs = (OpCode >> 16) & 0x1f; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeMemPair.cs b/ChocolArm64/Decoder/AOpCodeMemPair.cs deleted file mode 100644 index ec866c84e5..0000000000 --- a/ChocolArm64/Decoder/AOpCodeMemPair.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMemPair : AOpCodeMemImm - { - public int Rt2 { get; private set; } - - public AOpCodeMemPair(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt2 = (OpCode >> 10) & 0x1f; - WBack = ((OpCode >> 23) & 0x1) != 0; - PostIdx = ((OpCode >> 23) & 0x3) == 1; - Extend64 = ((OpCode >> 30) & 0x3) == 1; - Size = ((OpCode >> 31) & 0x1) | 2; - - DecodeImm(OpCode); - } - - protected void DecodeImm(int OpCode) - { - Imm = ((long)(OpCode >> 15) << 57) >> (57 - Size); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeMemReg.cs b/ChocolArm64/Decoder/AOpCodeMemReg.cs deleted file mode 100644 index 989271282f..0000000000 --- a/ChocolArm64/Decoder/AOpCodeMemReg.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMemReg : AOpCodeMem - { - public bool Shift { get; private set; } - public int Rm { get; private set; } - - public AIntType IntType { get; private set; } - - public AOpCodeMemReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Shift = ((OpCode >> 12) & 0x1) != 0; - IntType = (AIntType)((OpCode >> 13) & 0x7); - Rm = (OpCode >> 16) & 0x1f; - Extend64 = ((OpCode >> 22) & 0x3) == 2; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeMov.cs b/ChocolArm64/Decoder/AOpCodeMov.cs deleted file mode 100644 index d5398646d1..0000000000 --- a/ChocolArm64/Decoder/AOpCodeMov.cs +++ /dev/null @@ -1,36 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMov : AOpCode - { - public int Rd { get; private set; } - public long Imm { get; private set; } - public int Pos { get; private set; } - - public AOpCodeMov(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int P1 = (OpCode >> 22) & 1; - int SF = (OpCode >> 31) & 1; - - if (SF == 0 && P1 != 0) - { - Emitter = AInstEmit.Und; - - return; - } - - Rd = (OpCode >> 0) & 0x1f; - Imm = (OpCode >> 5) & 0xffff; - Pos = (OpCode >> 21) & 0x3; - - Pos <<= 4; - Imm <<= Pos; - - RegisterSize = (OpCode >> 31) != 0 - ? ARegisterSize.Int64 - : ARegisterSize.Int32; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeMul.cs b/ChocolArm64/Decoder/AOpCodeMul.cs deleted file mode 100644 index ca2b0cdb38..0000000000 --- a/ChocolArm64/Decoder/AOpCodeMul.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeMul : AOpCodeAlu - { - public int Rm { get; private set; } - public int Ra { get; private set; } - - public AOpCodeMul(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Ra = (OpCode >> 10) & 0x1f; - Rm = (OpCode >> 16) & 0x1f; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimd.cs b/ChocolArm64/Decoder/AOpCodeSimd.cs deleted file mode 100644 index 4170851721..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimd.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimd : AOpCode, IAOpCodeSimd - { - public int Rd { get; private set; } - public int Rn { get; private set; } - public int Opc { get; private set; } - public int Size { get; protected set; } - - public AOpCodeSimd(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rd = (OpCode >> 0) & 0x1f; - Rn = (OpCode >> 5) & 0x1f; - Opc = (OpCode >> 15) & 0x3; - Size = (OpCode >> 22) & 0x3; - - RegisterSize = ((OpCode >> 30) & 1) != 0 - ? ARegisterSize.SIMD128 - : ARegisterSize.SIMD64; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdCvt.cs b/ChocolArm64/Decoder/AOpCodeSimdCvt.cs deleted file mode 100644 index 41f4d3b143..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdCvt.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdCvt : AOpCodeSimd - { - public int FBits { get; private set; } - - public AOpCodeSimdCvt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - //TODO: - //Und of Fixed Point variants. - int Scale = (OpCode >> 10) & 0x3f; - int SF = (OpCode >> 31) & 0x1; - - /*if (Type != SF && !(Type == 2 && SF == 1)) - { - Emitter = AInstEmit.Und; - - return; - }*/ - - FBits = 64 - Scale; - - RegisterSize = SF != 0 - ? ARegisterSize.Int64 - : ARegisterSize.Int32; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdExt.cs b/ChocolArm64/Decoder/AOpCodeSimdExt.cs deleted file mode 100644 index 888e447030..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdExt.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdExt : AOpCodeSimdReg - { - public int Imm4 { get; private set; } - - public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Imm4 = (OpCode >> 11) & 0xf; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdFcond.cs b/ChocolArm64/Decoder/AOpCodeSimdFcond.cs deleted file mode 100644 index e38e742473..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdFcond.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdFcond : AOpCodeSimdReg, IAOpCodeCond - { - public int NZCV { get; private set; } - - public ACond Cond { get; private set; } - - public AOpCodeSimdFcond(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - NZCV = (OpCode >> 0) & 0xf; - Cond = (ACond)((OpCode >> 12) & 0xf); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdFmov.cs b/ChocolArm64/Decoder/AOpCodeSimdFmov.cs deleted file mode 100644 index 3f88895985..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdFmov.cs +++ /dev/null @@ -1,33 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdFmov : AOpCode, IAOpCodeSimd - { - public int Rd { get; private set; } - public long Imm { get; private set; } - public int Size { get; private set; } - - public AOpCodeSimdFmov(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Imm5 = (OpCode >> 5) & 0x1f; - int Type = (OpCode >> 22) & 0x3; - - if (Imm5 != 0b00000 || Type > 1) - { - Emitter = AInstEmit.Und; - - return; - } - - Size = Type; - - long Imm; - - Rd = (OpCode >> 0) & 0x1f; - Imm = (OpCode >> 13) & 0xff; - - this.Imm = ADecoderHelper.DecodeImm8Float(Imm, Type); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdImm.cs b/ChocolArm64/Decoder/AOpCodeSimdImm.cs deleted file mode 100644 index e7dfe62114..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdImm.cs +++ /dev/null @@ -1,101 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdImm : AOpCode, IAOpCodeSimd - { - public int Rd { get; private set; } - public long Imm { get; private set; } - public int Size { get; private set; } - - public AOpCodeSimdImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rd = OpCode & 0x1f; - - int CMode = (OpCode >> 12) & 0xf; - int Op = (OpCode >> 29) & 0x1; - - int ModeLow = CMode & 1; - int ModeHigh = CMode >> 1; - - long Imm; - - Imm = ((uint)OpCode >> 5) & 0x1f; - Imm |= ((uint)OpCode >> 11) & 0xe0; - - if (ModeHigh == 0b111) - { - Size = ModeLow != 0 ? Op : 3; - - switch (Op | (ModeLow << 1)) - { - case 0: - //64-bits Immediate. - //Transform abcd efgh into abcd efgh abcd efgh ... - Imm = (long)((ulong)Imm * 0x0101010101010101); - break; - - case 1: - //64-bits Immediate. - //Transform abcd efgh into aaaa aaaa bbbb bbbb ... - Imm = (Imm & 0xf0) >> 4 | (Imm & 0x0f) << 4; - Imm = (Imm & 0xcc) >> 2 | (Imm & 0x33) << 2; - Imm = (Imm & 0xaa) >> 1 | (Imm & 0x55) << 1; - - Imm = (long)((ulong)Imm * 0x8040201008040201); - Imm = (long)((ulong)Imm & 0x8080808080808080); - - Imm |= Imm >> 4; - Imm |= Imm >> 2; - Imm |= Imm >> 1; - break; - - case 2: - case 3: - //Floating point Immediate. - Imm = ADecoderHelper.DecodeImm8Float(Imm, Size); - break; - } - } - else if ((ModeHigh & 0b110) == 0b100) - { - //16-bits shifted Immediate. - Size = 1; Imm <<= (ModeHigh & 1) << 3; - } - else if ((ModeHigh & 0b100) == 0b000) - { - //32-bits shifted Immediate. - Size = 2; Imm <<= ModeHigh << 3; - } - else if ((ModeHigh & 0b111) == 0b110) - { - //32-bits shifted Immediate (fill with ones). - Size = 2; Imm = ShlOnes(Imm, 8 << ModeLow); - } - else - { - //8 bits without shift. - Size = 0; - } - - this.Imm = Imm; - - RegisterSize = ((OpCode >> 30) & 1) != 0 - ? ARegisterSize.SIMD128 - : ARegisterSize.SIMD64; - } - - private static long ShlOnes(long Value, int Shift) - { - if (Shift != 0) - { - return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift)); - } - else - { - return Value; - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdIns.cs b/ChocolArm64/Decoder/AOpCodeSimdIns.cs deleted file mode 100644 index 0b60bbe837..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdIns.cs +++ /dev/null @@ -1,36 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdIns : AOpCodeSimd - { - public int SrcIndex { get; private set; } - public int DstIndex { get; private set; } - - public AOpCodeSimdIns(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Imm4 = (OpCode >> 11) & 0xf; - int Imm5 = (OpCode >> 16) & 0x1f; - - if (Imm5 == 0b10000) - { - Emitter = AInstEmit.Und; - - return; - } - - Size = Imm5 & -Imm5; - - switch (Size) - { - case 1: Size = 0; break; - case 2: Size = 1; break; - case 4: Size = 2; break; - case 8: Size = 3; break; - } - - SrcIndex = Imm4 >> Size; - DstIndex = Imm5 >> (Size + 1); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdMemImm.cs b/ChocolArm64/Decoder/AOpCodeSimdMemImm.cs deleted file mode 100644 index 1ef19a5d67..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdMemImm.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemImm : AOpCodeMemImm, IAOpCodeSimd - { - public AOpCodeSimdMemImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Size |= (OpCode >> 21) & 4; - - if (!WBack && !Unscaled && Size >= 4) - { - Imm <<= 4; - } - - Extend64 = false; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdMemLit.cs b/ChocolArm64/Decoder/AOpCodeSimdMemLit.cs deleted file mode 100644 index ea6fe00be9..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdMemLit.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemLit : AOpCode, IAOpCodeSimd, IAOpCodeLit - { - public int Rt { get; private set; } - public long Imm { get; private set; } - public int Size { get; private set; } - public bool Signed => false; - public bool Prefetch => false; - - public AOpCodeSimdMemLit(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Opc = (OpCode >> 30) & 3; - - if (Opc == 3) - { - Emitter = AInstEmit.Und; - - return; - } - - Rt = OpCode & 0x1f; - - Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); - - Size = Opc + 2; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdMemPair.cs b/ChocolArm64/Decoder/AOpCodeSimdMemPair.cs deleted file mode 100644 index db99e3d44d..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdMemPair.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemPair : AOpCodeMemPair, IAOpCodeSimd - { - public AOpCodeSimdMemPair(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Size = ((OpCode >> 30) & 3) + 2; - - Extend64 = false; - - DecodeImm(OpCode); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdMemReg.cs b/ChocolArm64/Decoder/AOpCodeSimdMemReg.cs deleted file mode 100644 index aabf484611..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdMemReg.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemReg : AOpCodeMemReg, IAOpCodeSimd - { - public AOpCodeSimdMemReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Size |= (OpCode >> 21) & 4; - - Extend64 = false; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdMemSs.cs b/ChocolArm64/Decoder/AOpCodeSimdMemSs.cs deleted file mode 100644 index c8794ff5a5..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdMemSs.cs +++ /dev/null @@ -1,98 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdMemSs : AOpCodeMemReg, IAOpCodeSimd - { - public int SElems { get; private set; } - public int Index { get; private set; } - public bool Replicate { get; private set; } - public bool WBack { get; private set; } - - public AOpCodeSimdMemSs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - int Size = (OpCode >> 10) & 3; - int S = (OpCode >> 12) & 1; - int SElems = (OpCode >> 12) & 2; - int Scale = (OpCode >> 14) & 3; - int L = (OpCode >> 22) & 1; - int Q = (OpCode >> 30) & 1; - - SElems |= (OpCode >> 21) & 1; - - SElems++; - - int Index = (Q << 3) | (S << 2) | Size; - - switch (Scale) - { - case 1: - { - if ((Size & 1) != 0) - { - Inst = AInst.Undefined; - - return; - } - - Index >>= 1; - - break; - } - - case 2: - { - if ((Size & 2) != 0 || - ((Size & 1) != 0 && S != 0)) - { - Inst = AInst.Undefined; - - return; - } - - if ((Size & 1) != 0) - { - Index >>= 3; - - Scale = 3; - } - else - { - Index >>= 2; - } - - break; - } - - case 3: - { - if (L == 0 || S != 0) - { - Inst = AInst.Undefined; - - return; - } - - Scale = Size; - - Replicate = true; - - break; - } - } - - this.Index = Index; - this.SElems = SElems; - this.Size = Scale; - - Extend64 = false; - - WBack = ((OpCode >> 23) & 1) != 0; - - RegisterSize = Q != 0 - ? ARegisterSize.SIMD128 - : ARegisterSize.SIMD64; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdReg.cs b/ChocolArm64/Decoder/AOpCodeSimdReg.cs deleted file mode 100644 index 702ffed1e2..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdReg.cs +++ /dev/null @@ -1,18 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdReg : AOpCodeSimd - { - public bool Bit3 { get; private set; } - public int Ra { get; private set; } - public int Rm { get; protected set; } - - public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Bit3 = ((OpCode >> 3) & 0x1) != 0; - Ra = (OpCode >> 10) & 0x1f; - Rm = (OpCode >> 16) & 0x1f; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs deleted file mode 100644 index d6dc4bd231..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdRegElem : AOpCodeSimdReg - { - public int Index { get; private set; } - - public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - switch (Size) - { - case 1: - Index = (OpCode >> 20) & 3 | - (OpCode >> 9) & 4; - - Rm &= 0xf; - - break; - - case 2: - Index = (OpCode >> 21) & 1 | - (OpCode >> 10) & 2; - - break; - - default: Emitter = AInstEmit.Und; return; - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs deleted file mode 100644 index e61d7093a7..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs +++ /dev/null @@ -1,22 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdRegElemF : AOpCodeSimdReg - { - public int Index { get; private set; } - - public AOpCodeSimdRegElemF(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - if ((Size & 1) != 0) - { - Index = (OpCode >> 11) & 1; - } - else - { - Index = (OpCode >> 21) & 1 | - (OpCode >> 10) & 2; - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdShImm.cs b/ChocolArm64/Decoder/AOpCodeSimdShImm.cs deleted file mode 100644 index e6d5210f2f..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdShImm.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdShImm : AOpCodeSimd - { - public int Imm { get; private set; } - - public AOpCodeSimdShImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Imm = (OpCode >> 16) & 0x7f; - - Size = ABitUtils.HighestBitSetNibble(Imm >> 3); - } - } -} diff --git a/ChocolArm64/Decoder/AOpCodeSimdTbl.cs b/ChocolArm64/Decoder/AOpCodeSimdTbl.cs deleted file mode 100644 index c8ae5bac74..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSimdTbl.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSimdTbl : AOpCodeSimdReg - { - public AOpCodeSimdTbl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Size = ((OpCode >> 13) & 3) + 1; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSystem.cs b/ChocolArm64/Decoder/AOpCodeSystem.cs deleted file mode 100644 index 3d81a5d451..0000000000 --- a/ChocolArm64/Decoder/AOpCodeSystem.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder -{ - class AOpCodeSystem : AOpCode - { - public int Rt { get; private set; } - public int Op2 { get; private set; } - public int CRm { get; private set; } - public int CRn { get; private set; } - public int Op1 { get; private set; } - public int Op0 { get; private set; } - - public AOpCodeSystem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Rt = (OpCode >> 0) & 0x1f; - Op2 = (OpCode >> 5) & 0x7; - CRm = (OpCode >> 8) & 0xf; - CRn = (OpCode >> 12) & 0xf; - Op1 = (OpCode >> 16) & 0x7; - Op0 = ((OpCode >> 19) & 0x1) | 2; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/IAOpCode.cs b/ChocolArm64/Decoder/IAOpCode.cs deleted file mode 100644 index 44bf9cb2f1..0000000000 --- a/ChocolArm64/Decoder/IAOpCode.cs +++ /dev/null @@ -1,13 +0,0 @@ -using ChocolArm64.Instruction; -using ChocolArm64.State; - -namespace ChocolArm64.Decoder -{ - interface IAOpCode - { - long Position { get; } - - AInstEmitter Emitter { get; } - ARegisterSize RegisterSize { get; } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/IAOpCodeAlu.cs b/ChocolArm64/Decoder/IAOpCodeAlu.cs deleted file mode 100644 index 22af4c82d6..0000000000 --- a/ChocolArm64/Decoder/IAOpCodeAlu.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeAlu : IAOpCode - { - int Rd { get; } - int Rn { get; } - - ADataOp DataOp { get; } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/IAOpCodeAluImm.cs b/ChocolArm64/Decoder/IAOpCodeAluImm.cs deleted file mode 100644 index 04b5c5f7d1..0000000000 --- a/ChocolArm64/Decoder/IAOpCodeAluImm.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeAluImm : IAOpCodeAlu - { - long Imm { get; } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/IAOpCodeAluRs.cs b/ChocolArm64/Decoder/IAOpCodeAluRs.cs deleted file mode 100644 index 5ca9de4032..0000000000 --- a/ChocolArm64/Decoder/IAOpCodeAluRs.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeAluRs : IAOpCodeAlu - { - int Shift { get; } - int Rm { get; } - - AShiftType ShiftType { get; } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/IAOpCodeAluRx.cs b/ChocolArm64/Decoder/IAOpCodeAluRx.cs deleted file mode 100644 index b49d5325a9..0000000000 --- a/ChocolArm64/Decoder/IAOpCodeAluRx.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeAluRx : IAOpCodeAlu - { - int Shift { get; } - int Rm { get; } - - AIntType IntType { get; } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/IAOpCodeCond.cs b/ChocolArm64/Decoder/IAOpCodeCond.cs deleted file mode 100644 index 1655abaac0..0000000000 --- a/ChocolArm64/Decoder/IAOpCodeCond.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeCond : IAOpCode - { - ACond Cond { get; } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder/IAOpCodeSimd.cs b/ChocolArm64/Decoder/IAOpCodeSimd.cs deleted file mode 100644 index 19032ad940..0000000000 --- a/ChocolArm64/Decoder/IAOpCodeSimd.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Decoder -{ - interface IAOpCodeSimd : IAOpCode - { - int Size { get; } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder32/A32OpCode.cs b/ChocolArm64/Decoder32/A32OpCode.cs deleted file mode 100644 index 56f870df6c..0000000000 --- a/ChocolArm64/Decoder32/A32OpCode.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder32 -{ - class A32OpCode : AOpCode - { - public ACond Cond { get; private set; } - - public A32OpCode(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Cond = (ACond)((uint)OpCode >> 28); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoder32/A32OpCodeBImmAl.cs b/ChocolArm64/Decoder32/A32OpCodeBImmAl.cs deleted file mode 100644 index 71bca7f95d..0000000000 --- a/ChocolArm64/Decoder32/A32OpCodeBImmAl.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ChocolArm64.Instruction; - -namespace ChocolArm64.Decoder32 -{ - class A32OpCodeBImmAl : A32OpCode - { - public int Imm; - public int H; - - public A32OpCodeBImmAl(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) - { - Imm = (OpCode << 8) >> 6; - H = (OpCode >> 23) & 2; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Decoders/Block.cs b/ChocolArm64/Decoders/Block.cs new file mode 100644 index 0000000000..c89ea7c6fb --- /dev/null +++ b/ChocolArm64/Decoders/Block.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Decoders +{ + class Block + { + public long Position { get; set; } + public long EndPosition { get; set; } + + public Block Next { get; set; } + public Block Branch { get; set; } + + public List OpCodes { get; private set; } + + public Block() + { + OpCodes = new List(); + } + + public Block(long position) : this() + { + Position = position; + } + + public OpCode64 GetLastOp() + { + if (OpCodes.Count > 0) + { + return OpCodes[OpCodes.Count - 1]; + } + + return null; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/Cond.cs b/ChocolArm64/Decoders/Cond.cs new file mode 100644 index 0000000000..57e12cd609 --- /dev/null +++ b/ChocolArm64/Decoders/Cond.cs @@ -0,0 +1,22 @@ +namespace ChocolArm64.Decoders +{ + enum Cond + { + Eq = 0, + Ne = 1, + GeUn = 2, + LtUn = 3, + Mi = 4, + Pl = 5, + Vs = 6, + Vc = 7, + GtUn = 8, + LeUn = 9, + Ge = 10, + Lt = 11, + Gt = 12, + Le = 13, + Al = 14, + Nv = 15 + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoder/ADataOp.cs b/ChocolArm64/Decoders/DataOp.cs similarity index 70% rename from ChocolArm64/Decoder/ADataOp.cs rename to ChocolArm64/Decoders/DataOp.cs index a5601a3abc..b7768bb4d0 100644 --- a/ChocolArm64/Decoder/ADataOp.cs +++ b/ChocolArm64/Decoders/DataOp.cs @@ -1,6 +1,6 @@ -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - enum ADataOp + enum DataOp { Adr = 0, Arithmetic = 1, diff --git a/ChocolArm64/Decoders/Decoder.cs b/ChocolArm64/Decoders/Decoder.cs new file mode 100644 index 0000000000..db43ac4fe8 --- /dev/null +++ b/ChocolArm64/Decoders/Decoder.cs @@ -0,0 +1,239 @@ +using ChocolArm64.Instructions; +using ChocolArm64.Memory; +using ChocolArm64.State; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection.Emit; + +namespace ChocolArm64.Decoders +{ + static class Decoder + { + private delegate object OpActivator(Inst inst, long position, int opCode); + + private static ConcurrentDictionary _opActivators; + + static Decoder() + { + _opActivators = new ConcurrentDictionary(); + } + + public static Block DecodeBasicBlock(CpuThreadState state, MemoryManager memory, long start) + { + Block block = new Block(start); + + FillBlock(state, memory, block); + + return block; + } + + public static (Block[] Graph, Block Root) DecodeSubroutine( + TranslatorCache cache, + CpuThreadState state, + MemoryManager memory, + long start) + { + Dictionary visited = new Dictionary(); + Dictionary visitedEnd = new Dictionary(); + + Queue blocks = new Queue(); + + Block Enqueue(long position) + { + if (!visited.TryGetValue(position, out Block output)) + { + output = new Block(position); + + blocks.Enqueue(output); + + visited.Add(position, output); + } + + return output; + } + + Block root = Enqueue(start); + + while (blocks.Count > 0) + { + Block current = blocks.Dequeue(); + + FillBlock(state, memory, current); + + //Set child blocks. "Branch" is the block the branch instruction + //points to (when taken), "Next" is the block at the next address, + //executed when the branch is not taken. For Unconditional Branches + //(except BL/BLR that are sub calls) or end of executable, Next is null. + if (current.OpCodes.Count > 0) + { + bool hasCachedSub = false; + + OpCode64 lastOp = current.GetLastOp(); + + if (lastOp is OpCodeBImm64 op) + { + if (op.Emitter == InstEmit.Bl) + { + hasCachedSub = cache.HasSubroutine(op.Imm); + } + else + { + current.Branch = Enqueue(op.Imm); + } + } + + if (!((lastOp is OpCodeBImmAl64) || + (lastOp is OpCodeBReg64)) || hasCachedSub) + { + current.Next = Enqueue(current.EndPosition); + } + } + + //If we have on the graph two blocks with the same end position, + //then we need to split the bigger block and have two small blocks, + //the end position of the bigger "Current" block should then be == to + //the position of the "Smaller" block. + while (visitedEnd.TryGetValue(current.EndPosition, out Block smaller)) + { + if (current.Position > smaller.Position) + { + Block temp = smaller; + + smaller = current; + current = temp; + } + + current.EndPosition = smaller.Position; + current.Next = smaller; + current.Branch = null; + + current.OpCodes.RemoveRange( + current.OpCodes.Count - smaller.OpCodes.Count, + smaller.OpCodes.Count); + + visitedEnd[smaller.EndPosition] = smaller; + } + + visitedEnd.Add(current.EndPosition, current); + } + + //Make and sort Graph blocks array by position. + Block[] graph = new Block[visited.Count]; + + while (visited.Count > 0) + { + ulong firstPos = ulong.MaxValue; + + foreach (Block block in visited.Values) + { + if (firstPos > (ulong)block.Position) + firstPos = (ulong)block.Position; + } + + Block current = visited[(long)firstPos]; + + do + { + graph[graph.Length - visited.Count] = current; + + visited.Remove(current.Position); + + current = current.Next; + } + while (current != null); + } + + return (graph, root); + } + + private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block) + { + long position = block.Position; + + OpCode64 opCode; + + do + { + //TODO: This needs to be changed to support both AArch32 and AArch64, + //once JIT support is introduced on AArch32 aswell. + opCode = DecodeOpCode(state, memory, position); + + block.OpCodes.Add(opCode); + + position += 4; + } + while (!(IsBranch(opCode) || IsException(opCode))); + + block.EndPosition = position; + } + + private static bool IsBranch(OpCode64 opCode) + { + return opCode is OpCodeBImm64 || + opCode is OpCodeBReg64; + } + + private static bool IsException(OpCode64 opCode) + { + return opCode.Emitter == InstEmit.Brk || + opCode.Emitter == InstEmit.Svc || + opCode.Emitter == InstEmit.Und; + } + + public static OpCode64 DecodeOpCode(CpuThreadState state, MemoryManager memory, long position) + { + int opCode = memory.ReadInt32(position); + + Inst inst; + + if (state.ExecutionMode == ExecutionMode.AArch64) + { + inst = OpCodeTable.GetInstA64(opCode); + } + else + { + //TODO: Thumb support. + inst = OpCodeTable.GetInstA32(opCode); + } + + OpCode64 decodedOpCode = new OpCode64(Inst.Undefined, position, opCode); + + if (inst.Type != null) + { + decodedOpCode = MakeOpCode(inst.Type, inst, position, opCode); + } + + return decodedOpCode; + } + + private static OpCode64 MakeOpCode(Type type, Inst inst, long position, int opCode) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + OpActivator createInstance = _opActivators.GetOrAdd(type, CacheOpActivator); + + return (OpCode64)createInstance(inst, position, opCode); + } + + private static OpActivator CacheOpActivator(Type type) + { + Type[] argTypes = new Type[] { typeof(Inst), typeof(long), typeof(int) }; + + DynamicMethod mthd = new DynamicMethod($"Make{type.Name}", type, argTypes); + + ILGenerator generator = mthd.GetILGenerator(); + + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldarg_2); + generator.Emit(OpCodes.Newobj, type.GetConstructor(argTypes)); + generator.Emit(OpCodes.Ret); + + return (OpActivator)mthd.CreateDelegate(typeof(OpActivator)); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/DecoderHelper.cs b/ChocolArm64/Decoders/DecoderHelper.cs new file mode 100644 index 0000000000..6ee279d7d6 --- /dev/null +++ b/ChocolArm64/Decoders/DecoderHelper.cs @@ -0,0 +1,107 @@ +using System; + +namespace ChocolArm64.Decoders +{ + static class DecoderHelper + { + public struct BitMask + { + public long WMask; + public long TMask; + public int Pos; + public int Shift; + public bool IsUndefined; + + public static BitMask Invalid => new BitMask { IsUndefined = true }; + } + + public static BitMask DecodeBitMask(int opCode, bool immediate) + { + int immS = (opCode >> 10) & 0x3f; + int immR = (opCode >> 16) & 0x3f; + + int n = (opCode >> 22) & 1; + int sf = (opCode >> 31) & 1; + + int length = BitUtils.HighestBitSet32((~immS & 0x3f) | (n << 6)); + + if (length < 1 || (sf == 0 && n != 0)) + { + return BitMask.Invalid; + } + + int size = 1 << length; + + int levels = size - 1; + + int s = immS & levels; + int r = immR & levels; + + if (immediate && s == levels) + { + return BitMask.Invalid; + } + + long wMask = BitUtils.FillWithOnes(s + 1); + long tMask = BitUtils.FillWithOnes(((s - r) & levels) + 1); + + if (r > 0) + { + wMask = BitUtils.RotateRight(wMask, r, size); + wMask &= BitUtils.FillWithOnes(size); + } + + return new BitMask() + { + WMask = BitUtils.Replicate(wMask, size), + TMask = BitUtils.Replicate(tMask, size), + + Pos = immS, + Shift = immR + }; + } + + public static long DecodeImm8Float(long imm, int size) + { + int e = 0, f = 0; + + switch (size) + { + case 0: e = 8; f = 23; break; + case 1: e = 11; f = 52; break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + + long value = (imm & 0x3f) << f - 4; + + long eBit = (imm >> 6) & 1; + long sBit = (imm >> 7) & 1; + + if (eBit != 0) + { + value |= (1L << e - 3) - 1 << f + 2; + } + + value |= (eBit ^ 1) << f + e - 1; + value |= sBit << f + e; + + return value; + } + + public static long DecodeImm26_2(int opCode) + { + return ((long)opCode << 38) >> 36; + } + + public static long DecodeImmS19_2(int opCode) + { + return (((long)opCode << 40) >> 43) & ~3; + } + + public static long DecodeImmS14_2(int opCode) + { + return (((long)opCode << 45) >> 48) & ~3; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCode64.cs b/ChocolArm64/Decoders/IOpCode64.cs new file mode 100644 index 0000000000..e940712311 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCode64.cs @@ -0,0 +1,13 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + interface IOpCode64 + { + long Position { get; } + + InstEmitter Emitter { get; } + RegisterSize RegisterSize { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeAlu64.cs b/ChocolArm64/Decoders/IOpCodeAlu64.cs new file mode 100644 index 0000000000..b9a5fe9e68 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeAlu64.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAlu64 : IOpCode64 + { + int Rd { get; } + int Rn { get; } + + DataOp DataOp { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeAluImm64.cs b/ChocolArm64/Decoders/IOpCodeAluImm64.cs new file mode 100644 index 0000000000..4b305e27b7 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeAluImm64.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAluImm64 : IOpCodeAlu64 + { + long Imm { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeAluRs64.cs b/ChocolArm64/Decoders/IOpCodeAluRs64.cs new file mode 100644 index 0000000000..df503ae9d8 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeAluRs64.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAluRs64 : IOpCodeAlu64 + { + int Shift { get; } + int Rm { get; } + + ShiftType ShiftType { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeAluRx64.cs b/ChocolArm64/Decoders/IOpCodeAluRx64.cs new file mode 100644 index 0000000000..f41fc4d2f0 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeAluRx64.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeAluRx64 : IOpCodeAlu64 + { + int Shift { get; } + int Rm { get; } + + IntType IntType { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/IOpCodeCond64.cs b/ChocolArm64/Decoders/IOpCodeCond64.cs new file mode 100644 index 0000000000..9c39d63329 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeCond64.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeCond64 : IOpCode64 + { + Cond Cond { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoder/IAOpCodeLit.cs b/ChocolArm64/Decoders/IOpCodeLit64.cs similarity index 70% rename from ChocolArm64/Decoder/IAOpCodeLit.cs rename to ChocolArm64/Decoders/IOpCodeLit64.cs index 0f5092d076..c6dc2c7fc1 100644 --- a/ChocolArm64/Decoder/IAOpCodeLit.cs +++ b/ChocolArm64/Decoders/IOpCodeLit64.cs @@ -1,6 +1,6 @@ -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - interface IAOpCodeLit : IAOpCode + interface IOpCodeLit64 : IOpCode64 { int Rt { get; } long Imm { get; } diff --git a/ChocolArm64/Decoders/IOpCodeSimd64.cs b/ChocolArm64/Decoders/IOpCodeSimd64.cs new file mode 100644 index 0000000000..fc8f54d608 --- /dev/null +++ b/ChocolArm64/Decoders/IOpCodeSimd64.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Decoders +{ + interface IOpCodeSimd64 : IOpCode64 + { + int Size { get; } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AIntType.cs b/ChocolArm64/Decoders/IntType.cs similarity index 78% rename from ChocolArm64/Decoder/AIntType.cs rename to ChocolArm64/Decoders/IntType.cs index 242fdada14..70f833ec56 100644 --- a/ChocolArm64/Decoder/AIntType.cs +++ b/ChocolArm64/Decoders/IntType.cs @@ -1,6 +1,6 @@ -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - enum AIntType + enum IntType { UInt8 = 0, UInt16 = 1, diff --git a/ChocolArm64/Decoders/OpCode64.cs b/ChocolArm64/Decoders/OpCode64.cs new file mode 100644 index 0000000000..b2dc363b8b --- /dev/null +++ b/ChocolArm64/Decoders/OpCode64.cs @@ -0,0 +1,40 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; +using System; + +namespace ChocolArm64.Decoders +{ + class OpCode64 : IOpCode64 + { + public long Position { get; private set; } + public int RawOpCode { get; private set; } + + public InstEmitter Emitter { get; protected set; } + public InstInterpreter Interpreter { get; protected set; } + public RegisterSize RegisterSize { get; protected set; } + + public OpCode64(Inst inst, long position, int opCode) + { + Position = position; + RawOpCode = opCode; + + RegisterSize = RegisterSize.Int64; + + Emitter = inst.Emitter; + Interpreter = inst.Interpreter; + } + + public int GetBitsCount() + { + switch (RegisterSize) + { + case RegisterSize.Int32: return 32; + case RegisterSize.Int64: return 64; + case RegisterSize.Simd64: return 64; + case RegisterSize.Simd128: return 128; + } + + throw new InvalidOperationException(); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeAdr64.cs b/ChocolArm64/Decoders/OpCodeAdr64.cs new file mode 100644 index 0000000000..98b2f07bbe --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAdr64.cs @@ -0,0 +1,18 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAdr64 : OpCode64 + { + public int Rd { get; private set; } + public long Imm { get; private set; } + + public OpCodeAdr64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = opCode & 0x1f; + + Imm = DecoderHelper.DecodeImmS19_2(opCode); + Imm |= ((long)opCode >> 29) & 3; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeAlu64.cs b/ChocolArm64/Decoders/OpCodeAlu64.cs new file mode 100644 index 0000000000..5f094572ef --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAlu64.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeAlu64 : OpCode64, IOpCodeAlu64 + { + public int Rd { get; protected set; } + public int Rn { get; private set; } + + public DataOp DataOp { get; private set; } + + public OpCodeAlu64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; + DataOp = (DataOp)((opCode >> 24) & 0x3); + + RegisterSize = (opCode >> 31) != 0 + ? State.RegisterSize.Int64 + : State.RegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeAluImm64.cs b/ChocolArm64/Decoders/OpCodeAluImm64.cs new file mode 100644 index 0000000000..64ac08a7a6 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAluImm64.cs @@ -0,0 +1,39 @@ +using ChocolArm64.Instructions; +using System; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluImm64 : OpCodeAlu64, IOpCodeAluImm64 + { + public long Imm { get; private set; } + + public OpCodeAluImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + if (DataOp == DataOp.Arithmetic) + { + Imm = (opCode >> 10) & 0xfff; + + int shift = (opCode >> 22) & 3; + + Imm <<= shift * 12; + } + else if (DataOp == DataOp.Logical) + { + var bm = DecoderHelper.DecodeBitMask(opCode, true); + + if (bm.IsUndefined) + { + Emitter = InstEmit.Und; + + return; + } + + Imm = bm.WMask; + } + else + { + throw new ArgumentException(nameof(opCode)); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeAluRs64.cs b/ChocolArm64/Decoders/OpCodeAluRs64.cs new file mode 100644 index 0000000000..f24c7f37bd --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAluRs64.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluRs64 : OpCodeAlu64, IOpCodeAluRs64 + { + public int Shift { get; private set; } + public int Rm { get; private set; } + + public ShiftType ShiftType { get; private set; } + + public OpCodeAluRs64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int shift = (opCode >> 10) & 0x3f; + + if (shift >= GetBitsCount()) + { + Emitter = InstEmit.Und; + + return; + } + + Shift = shift; + + Rm = (opCode >> 16) & 0x1f; + ShiftType = (ShiftType)((opCode >> 22) & 0x3); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeAluRx64.cs b/ChocolArm64/Decoders/OpCodeAluRx64.cs new file mode 100644 index 0000000000..a36f94ca89 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeAluRx64.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeAluRx64 : OpCodeAlu64, IOpCodeAluRx64 + { + public int Shift { get; private set; } + public int Rm { get; private set; } + + public IntType IntType { get; private set; } + + public OpCodeAluRx64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Shift = (opCode >> 10) & 0x7; + IntType = (IntType)((opCode >> 13) & 0x7); + Rm = (opCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBImm64.cs b/ChocolArm64/Decoders/OpCodeBImm64.cs new file mode 100644 index 0000000000..71c61bab3b --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBImm64.cs @@ -0,0 +1,11 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImm64 : OpCode64 + { + public long Imm { get; protected set; } + + public OpCodeBImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) { } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBImmAl64.cs b/ChocolArm64/Decoders/OpCodeBImmAl64.cs new file mode 100644 index 0000000000..f419ffa033 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBImmAl64.cs @@ -0,0 +1,12 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImmAl64 : OpCodeBImm64 + { + public OpCodeBImmAl64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm = position + DecoderHelper.DecodeImm26_2(opCode); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBImmCmp64.cs b/ChocolArm64/Decoders/OpCodeBImmCmp64.cs new file mode 100644 index 0000000000..6f43319918 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBImmCmp64.cs @@ -0,0 +1,21 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImmCmp64 : OpCodeBImm64 + { + public int Rt { get; private set; } + + public OpCodeBImmCmp64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = opCode & 0x1f; + + Imm = position + DecoderHelper.DecodeImmS19_2(opCode); + + RegisterSize = (opCode >> 31) != 0 + ? State.RegisterSize.Int64 + : State.RegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBImmCond64.cs b/ChocolArm64/Decoders/OpCodeBImmCond64.cs new file mode 100644 index 0000000000..227023092e --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBImmCond64.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImmCond64 : OpCodeBImm64, IOpCodeCond64 + { + public Cond Cond { get; private set; } + + public OpCodeBImmCond64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int o0 = (opCode >> 4) & 1; + + if (o0 != 0) + { + Emitter = InstEmit.Und; + + return; + } + + Cond = (Cond)(opCode & 0xf); + + Imm = position + DecoderHelper.DecodeImmS19_2(opCode); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBImmTest64.cs b/ChocolArm64/Decoders/OpCodeBImmTest64.cs new file mode 100644 index 0000000000..a2e8baeaeb --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBImmTest64.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBImmTest64 : OpCodeBImm64 + { + public int Rt { get; private set; } + public int Pos { get; private set; } + + public OpCodeBImmTest64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = opCode & 0x1f; + + Imm = position + DecoderHelper.DecodeImmS14_2(opCode); + + Pos = (opCode >> 19) & 0x1f; + Pos |= (opCode >> 26) & 0x20; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBReg64.cs b/ChocolArm64/Decoders/OpCodeBReg64.cs new file mode 100644 index 0000000000..74dbff5839 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBReg64.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBReg64 : OpCode64 + { + public int Rn { get; private set; } + + public OpCodeBReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int op4 = (opCode >> 0) & 0x1f; + int op2 = (opCode >> 16) & 0x1f; + + if (op2 != 0b11111 || op4 != 0b00000) + { + Emitter = InstEmit.Und; + + return; + } + + Rn = (opCode >> 5) & 0x1f; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeBfm64.cs b/ChocolArm64/Decoders/OpCodeBfm64.cs new file mode 100644 index 0000000000..6891a8f4fc --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeBfm64.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeBfm64 : OpCodeAlu64 + { + public long WMask { get; private set; } + public long TMask { get; private set; } + public int Pos { get; private set; } + public int Shift { get; private set; } + + public OpCodeBfm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + var bm = DecoderHelper.DecodeBitMask(opCode, false); + + if (bm.IsUndefined) + { + Emitter = InstEmit.Und; + + return; + } + + WMask = bm.WMask; + TMask = bm.TMask; + Pos = bm.Pos; + Shift = bm.Shift; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeCcmp64.cs b/ChocolArm64/Decoders/OpCodeCcmp64.cs new file mode 100644 index 0000000000..e2aae96dee --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeCcmp64.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeCcmp64 : OpCodeAlu64, IOpCodeCond64 + { + public int Nzcv { get; private set; } + protected int RmImm; + + public Cond Cond { get; private set; } + + public OpCodeCcmp64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int o3 = (opCode >> 4) & 1; + + if (o3 != 0) + { + Emitter = InstEmit.Und; + + return; + } + + Nzcv = (opCode >> 0) & 0xf; + Cond = (Cond)((opCode >> 12) & 0xf); + RmImm = (opCode >> 16) & 0x1f; + + Rd = CpuThreadState.ZrIndex; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeCcmpImm64.cs b/ChocolArm64/Decoders/OpCodeCcmpImm64.cs new file mode 100644 index 0000000000..78d5de5502 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeCcmpImm64.cs @@ -0,0 +1,11 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeCcmpImm64 : OpCodeCcmp64, IOpCodeAluImm64 + { + public long Imm => RmImm; + + public OpCodeCcmpImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) { } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeCcmpReg64.cs b/ChocolArm64/Decoders/OpCodeCcmpReg64.cs new file mode 100644 index 0000000000..a0544d987a --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeCcmpReg64.cs @@ -0,0 +1,15 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeCcmpReg64 : OpCodeCcmp64, IOpCodeAluRs64 + { + public int Rm => RmImm; + + public int Shift => 0; + + public ShiftType ShiftType => ShiftType.Lsl; + + public OpCodeCcmpReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) { } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeCsel64.cs b/ChocolArm64/Decoders/OpCodeCsel64.cs new file mode 100644 index 0000000000..d085a82378 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeCsel64.cs @@ -0,0 +1,17 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeCsel64 : OpCodeAlu64, IOpCodeCond64 + { + public int Rm { get; private set; } + + public Cond Cond { get; private set; } + + public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rm = (opCode >> 16) & 0x1f; + Cond = (Cond)((opCode >> 12) & 0xf); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeException64.cs b/ChocolArm64/Decoders/OpCodeException64.cs new file mode 100644 index 0000000000..2554124c78 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeException64.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeException64 : OpCode64 + { + public int Id { get; private set; } + + public OpCodeException64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Id = (opCode >> 5) & 0xffff; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeMem64.cs b/ChocolArm64/Decoders/OpCodeMem64.cs new file mode 100644 index 0000000000..36e6758352 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeMem64.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMem64 : OpCode64 + { + public int Rt { get; protected set; } + public int Rn { get; protected set; } + public int Size { get; protected set; } + public bool Extend64 { get; protected set; } + + public OpCodeMem64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; + Size = (opCode >> 30) & 0x3; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeMemEx64.cs b/ChocolArm64/Decoders/OpCodeMemEx64.cs new file mode 100644 index 0000000000..39935eb36e --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeMemEx64.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMemEx64 : OpCodeMem64 + { + public int Rt2 { get; private set; } + public int Rs { get; private set; } + + public OpCodeMemEx64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt2 = (opCode >> 10) & 0x1f; + Rs = (opCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeMemImm.cs b/ChocolArm64/Decoders/OpCodeMemImm64.cs similarity index 62% rename from ChocolArm64/Decoder/AOpCodeMemImm.cs rename to ChocolArm64/Decoders/OpCodeMemImm64.cs index 14edc51487..edaa4970ae 100644 --- a/ChocolArm64/Decoder/AOpCodeMemImm.cs +++ b/ChocolArm64/Decoders/OpCodeMemImm64.cs @@ -1,8 +1,8 @@ -using ChocolArm64.Instruction; +using ChocolArm64.Instructions; -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - class AOpCodeMemImm : AOpCodeMem + class OpCodeMemImm64 : OpCodeMem64 { public long Imm { get; protected set; } public bool WBack { get; protected set; } @@ -18,18 +18,18 @@ namespace ChocolArm64.Decoder Unsigned } - public AOpCodeMemImm(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + public OpCodeMemImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) { - Extend64 = ((OpCode >> 22) & 3) == 2; - WBack = ((OpCode >> 24) & 1) == 0; + Extend64 = ((opCode >> 22) & 3) == 2; + WBack = ((opCode >> 24) & 1) == 0; //The type is not valid for the Unsigned Immediate 12-bits encoding, //because the bits 11:10 are used for the larger Immediate offset. - MemOp Type = WBack ? (MemOp)((OpCode >> 10) & 3) : MemOp.Unsigned; + MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned; - PostIdx = Type == MemOp.PostIndexed; - Unscaled = Type == MemOp.Unscaled || - Type == MemOp.Unprivileged; + PostIdx = type == MemOp.PostIndexed; + Unscaled = type == MemOp.Unscaled || + type == MemOp.Unprivileged; //Unscaled and Unprivileged doesn't write back, //but they do use the 9-bits Signed Immediate. @@ -41,12 +41,12 @@ namespace ChocolArm64.Decoder if (WBack || Unscaled) { //9-bits Signed Immediate. - Imm = (OpCode << 43) >> 55; + Imm = (opCode << 43) >> 55; } else { //12-bits Unsigned Immediate. - Imm = ((OpCode >> 10) & 0xfff) << Size; + Imm = ((opCode >> 10) & 0xfff) << Size; } } } diff --git a/ChocolArm64/Decoder/AOpCodeMemLit.cs b/ChocolArm64/Decoders/OpCodeMemLit64.cs similarity index 63% rename from ChocolArm64/Decoder/AOpCodeMemLit.cs rename to ChocolArm64/Decoders/OpCodeMemLit64.cs index ad719a1942..29bfeee31f 100644 --- a/ChocolArm64/Decoder/AOpCodeMemLit.cs +++ b/ChocolArm64/Decoders/OpCodeMemLit64.cs @@ -1,8 +1,8 @@ -using ChocolArm64.Instruction; +using ChocolArm64.Instructions; -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - class AOpCodeMemLit : AOpCode, IAOpCodeLit + class OpCodeMemLit64 : OpCode64, IOpCodeLit64 { public int Rt { get; private set; } public long Imm { get; private set; } @@ -10,13 +10,13 @@ namespace ChocolArm64.Decoder public bool Signed { get; private set; } public bool Prefetch { get; private set; } - public AOpCodeMemLit(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + public OpCodeMemLit64(Inst inst, long position, int opCode) : base(inst, position, opCode) { - Rt = OpCode & 0x1f; + Rt = opCode & 0x1f; - Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode); + Imm = position + DecoderHelper.DecodeImmS19_2(opCode); - switch ((OpCode >> 30) & 3) + switch ((opCode >> 30) & 3) { case 0: Size = 2; Signed = false; Prefetch = false; break; case 1: Size = 3; Signed = false; Prefetch = false; break; diff --git a/ChocolArm64/Decoders/OpCodeMemPair64.cs b/ChocolArm64/Decoders/OpCodeMemPair64.cs new file mode 100644 index 0000000000..5b81c75589 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeMemPair64.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMemPair64 : OpCodeMemImm64 + { + public int Rt2 { get; private set; } + + public OpCodeMemPair64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt2 = (opCode >> 10) & 0x1f; + WBack = ((opCode >> 23) & 0x1) != 0; + PostIdx = ((opCode >> 23) & 0x3) == 1; + Extend64 = ((opCode >> 30) & 0x3) == 1; + Size = ((opCode >> 31) & 0x1) | 2; + + DecodeImm(opCode); + } + + protected void DecodeImm(int opCode) + { + Imm = ((long)(opCode >> 15) << 57) >> (57 - Size); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeMemReg64.cs b/ChocolArm64/Decoders/OpCodeMemReg64.cs new file mode 100644 index 0000000000..3dd210fbd2 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeMemReg64.cs @@ -0,0 +1,20 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMemReg64 : OpCodeMem64 + { + public bool Shift { get; private set; } + public int Rm { get; private set; } + + public IntType IntType { get; private set; } + + public OpCodeMemReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Shift = ((opCode >> 12) & 0x1) != 0; + IntType = (IntType)((opCode >> 13) & 0x7); + Rm = (opCode >> 16) & 0x1f; + Extend64 = ((opCode >> 22) & 0x3) == 2; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeMov64.cs b/ChocolArm64/Decoders/OpCodeMov64.cs new file mode 100644 index 0000000000..f969785479 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeMov64.cs @@ -0,0 +1,36 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeMov64 : OpCode64 + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Pos { get; private set; } + + public OpCodeMov64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int p1 = (opCode >> 22) & 1; + int sf = (opCode >> 31) & 1; + + if (sf == 0 && p1 != 0) + { + Emitter = InstEmit.Und; + + return; + } + + Rd = (opCode >> 0) & 0x1f; + Imm = (opCode >> 5) & 0xffff; + Pos = (opCode >> 21) & 0x3; + + Pos <<= 4; + Imm <<= Pos; + + RegisterSize = (opCode >> 31) != 0 + ? State.RegisterSize.Int64 + : State.RegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeMul64.cs b/ChocolArm64/Decoders/OpCodeMul64.cs new file mode 100644 index 0000000000..176b7b93b5 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeMul64.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeMul64 : OpCodeAlu64 + { + public int Rm { get; private set; } + public int Ra { get; private set; } + + public OpCodeMul64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Ra = (opCode >> 10) & 0x1f; + Rm = (opCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimd64.cs b/ChocolArm64/Decoders/OpCodeSimd64.cs new file mode 100644 index 0000000000..a705e489cc --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimd64.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimd64 : OpCode64, IOpCodeSimd64 + { + public int Rd { get; private set; } + public int Rn { get; private set; } + public int Opc { get; private set; } + public int Size { get; protected set; } + + public OpCodeSimd64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = (opCode >> 0) & 0x1f; + Rn = (opCode >> 5) & 0x1f; + Opc = (opCode >> 15) & 0x3; + Size = (opCode >> 22) & 0x3; + + RegisterSize = ((opCode >> 30) & 1) != 0 + ? RegisterSize.Simd128 + : RegisterSize.Simd64; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdCvt64.cs b/ChocolArm64/Decoders/OpCodeSimdCvt64.cs new file mode 100644 index 0000000000..6c68a3af26 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdCvt64.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdCvt64 : OpCodeSimd64 + { + public int FBits { get; private set; } + + public OpCodeSimdCvt64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + //TODO: + //Und of Fixed Point variants. + int scale = (opCode >> 10) & 0x3f; + int sf = (opCode >> 31) & 0x1; + + /*if (Type != SF && !(Type == 2 && SF == 1)) + { + Emitter = AInstEmit.Und; + + return; + }*/ + + FBits = 64 - scale; + + RegisterSize = sf != 0 + ? State.RegisterSize.Int64 + : State.RegisterSize.Int32; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdExt64.cs b/ChocolArm64/Decoders/OpCodeSimdExt64.cs new file mode 100644 index 0000000000..1c57f19cc4 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdExt64.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdExt64 : OpCodeSimdReg64 + { + public int Imm4 { get; private set; } + + public OpCodeSimdExt64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm4 = (opCode >> 11) & 0xf; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdFcond64.cs b/ChocolArm64/Decoders/OpCodeSimdFcond64.cs new file mode 100644 index 0000000000..b0f1c0eb93 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdFcond64.cs @@ -0,0 +1,17 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdFcond64 : OpCodeSimdReg64, IOpCodeCond64 + { + public int Nzcv { get; private set; } + + public Cond Cond { get; private set; } + + public OpCodeSimdFcond64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Nzcv = (opCode >> 0) & 0xf; + Cond = (Cond)((opCode >> 12) & 0xf); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdFmov64.cs b/ChocolArm64/Decoders/OpCodeSimdFmov64.cs new file mode 100644 index 0000000000..6752e185e5 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdFmov64.cs @@ -0,0 +1,33 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdFmov64 : OpCode64, IOpCodeSimd64 + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + + public OpCodeSimdFmov64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int imm5 = (opCode >> 5) & 0x1f; + int type = (opCode >> 22) & 0x3; + + if (imm5 != 0b00000 || type > 1) + { + Emitter = InstEmit.Und; + + return; + } + + Size = type; + + long imm; + + Rd = (opCode >> 0) & 0x1f; + imm = (opCode >> 13) & 0xff; + + Imm = DecoderHelper.DecodeImm8Float(imm, type); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdImm64.cs b/ChocolArm64/Decoders/OpCodeSimdImm64.cs new file mode 100644 index 0000000000..3ef6a8c6e2 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdImm64.cs @@ -0,0 +1,101 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdImm64 : OpCode64, IOpCodeSimd64 + { + public int Rd { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + + public OpCodeSimdImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rd = opCode & 0x1f; + + int cMode = (opCode >> 12) & 0xf; + int op = (opCode >> 29) & 0x1; + + int modeLow = cMode & 1; + int modeHigh = cMode >> 1; + + long imm; + + imm = ((uint)opCode >> 5) & 0x1f; + imm |= ((uint)opCode >> 11) & 0xe0; + + if (modeHigh == 0b111) + { + Size = modeLow != 0 ? op : 3; + + switch (op | (modeLow << 1)) + { + case 0: + //64-bits Immediate. + //Transform abcd efgh into abcd efgh abcd efgh ... + imm = (long)((ulong)imm * 0x0101010101010101); + break; + + case 1: + //64-bits Immediate. + //Transform abcd efgh into aaaa aaaa bbbb bbbb ... + imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4; + imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2; + imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1; + + imm = (long)((ulong)imm * 0x8040201008040201); + imm = (long)((ulong)imm & 0x8080808080808080); + + imm |= imm >> 4; + imm |= imm >> 2; + imm |= imm >> 1; + break; + + case 2: + case 3: + //Floating point Immediate. + imm = DecoderHelper.DecodeImm8Float(imm, Size); + break; + } + } + else if ((modeHigh & 0b110) == 0b100) + { + //16-bits shifted Immediate. + Size = 1; imm <<= (modeHigh & 1) << 3; + } + else if ((modeHigh & 0b100) == 0b000) + { + //32-bits shifted Immediate. + Size = 2; imm <<= modeHigh << 3; + } + else if ((modeHigh & 0b111) == 0b110) + { + //32-bits shifted Immediate (fill with ones). + Size = 2; imm = ShlOnes(imm, 8 << modeLow); + } + else + { + //8 bits without shift. + Size = 0; + } + + Imm = imm; + + RegisterSize = ((opCode >> 30) & 1) != 0 + ? State.RegisterSize.Simd128 + : State.RegisterSize.Simd64; + } + + private static long ShlOnes(long value, int shift) + { + if (shift != 0) + { + return value << shift | (long)(ulong.MaxValue >> (64 - shift)); + } + else + { + return value; + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdIns64.cs b/ChocolArm64/Decoders/OpCodeSimdIns64.cs new file mode 100644 index 0000000000..3b25faebfd --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdIns64.cs @@ -0,0 +1,36 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdIns64 : OpCodeSimd64 + { + public int SrcIndex { get; private set; } + public int DstIndex { get; private set; } + + public OpCodeSimdIns64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int imm4 = (opCode >> 11) & 0xf; + int imm5 = (opCode >> 16) & 0x1f; + + if (imm5 == 0b10000) + { + Emitter = InstEmit.Und; + + return; + } + + Size = imm5 & -imm5; + + switch (Size) + { + case 1: Size = 0; break; + case 2: Size = 1; break; + case 4: Size = 2; break; + case 8: Size = 3; break; + } + + SrcIndex = imm4 >> Size; + DstIndex = imm5 >> (Size + 1); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdMemImm64.cs b/ChocolArm64/Decoders/OpCodeSimdMemImm64.cs new file mode 100644 index 0000000000..9fbab5674f --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdMemImm64.cs @@ -0,0 +1,19 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemImm64 : OpCodeMemImm64, IOpCodeSimd64 + { + public OpCodeSimdMemImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Size |= (opCode >> 21) & 4; + + if (!WBack && !Unscaled && Size >= 4) + { + Imm <<= 4; + } + + Extend64 = false; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdMemLit64.cs b/ChocolArm64/Decoders/OpCodeSimdMemLit64.cs new file mode 100644 index 0000000000..c98ffd030b --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdMemLit64.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemLit64 : OpCode64, IOpCodeSimd64, IOpCodeLit64 + { + public int Rt { get; private set; } + public long Imm { get; private set; } + public int Size { get; private set; } + public bool Signed => false; + public bool Prefetch => false; + + public OpCodeSimdMemLit64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int opc = (opCode >> 30) & 3; + + if (opc == 3) + { + Emitter = InstEmit.Und; + + return; + } + + Rt = opCode & 0x1f; + + Imm = position + DecoderHelper.DecodeImmS19_2(opCode); + + Size = opc + 2; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdMemMs.cs b/ChocolArm64/Decoders/OpCodeSimdMemMs64.cs similarity index 55% rename from ChocolArm64/Decoder/AOpCodeSimdMemMs.cs rename to ChocolArm64/Decoders/OpCodeSimdMemMs64.cs index a54e2360c5..0748ef43f3 100644 --- a/ChocolArm64/Decoder/AOpCodeSimdMemMs.cs +++ b/ChocolArm64/Decoders/OpCodeSimdMemMs64.cs @@ -1,18 +1,18 @@ -using ChocolArm64.Instruction; +using ChocolArm64.Instructions; using ChocolArm64.State; -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - class AOpCodeSimdMemMs : AOpCodeMemReg, IAOpCodeSimd + class OpCodeSimdMemMs64 : OpCodeMemReg64, IOpCodeSimd64 { public int Reps { get; private set; } public int SElems { get; private set; } public int Elems { get; private set; } public bool WBack { get; private set; } - public AOpCodeSimdMemMs(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + public OpCodeSimdMemMs64(Inst inst, long position, int opCode) : base(inst, position, opCode) { - switch ((OpCode >> 12) & 0xf) + switch ((opCode >> 12) & 0xf) { case 0b0000: Reps = 1; SElems = 4; break; case 0b0010: Reps = 4; SElems = 1; break; @@ -22,26 +22,26 @@ namespace ChocolArm64.Decoder case 0b1000: Reps = 1; SElems = 2; break; case 0b1010: Reps = 2; SElems = 1; break; - default: Inst = AInst.Undefined; return; + default: inst = Inst.Undefined; return; } - Size = (OpCode >> 10) & 3; - WBack = ((OpCode >> 23) & 1) != 0; + Size = (opCode >> 10) & 3; + WBack = ((opCode >> 23) & 1) != 0; - bool Q = ((OpCode >> 30) & 1) != 0; + bool q = ((opCode >> 30) & 1) != 0; - if (!Q && Size == 3 && SElems != 1) + if (!q && Size == 3 && SElems != 1) { - Inst = AInst.Undefined; + inst = Inst.Undefined; return; } Extend64 = false; - RegisterSize = Q - ? ARegisterSize.SIMD128 - : ARegisterSize.SIMD64; + RegisterSize = q + ? State.RegisterSize.Simd128 + : State.RegisterSize.Simd64; Elems = (GetBitsCount() >> 3) >> Size; } diff --git a/ChocolArm64/Decoders/OpCodeSimdMemPair64.cs b/ChocolArm64/Decoders/OpCodeSimdMemPair64.cs new file mode 100644 index 0000000000..1b796742e3 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdMemPair64.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemPair64 : OpCodeMemPair64, IOpCodeSimd64 + { + public OpCodeSimdMemPair64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Size = ((opCode >> 30) & 3) + 2; + + Extend64 = false; + + DecodeImm(opCode); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdMemReg64.cs b/ChocolArm64/Decoders/OpCodeSimdMemReg64.cs new file mode 100644 index 0000000000..4ccbbed2da --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdMemReg64.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemReg64 : OpCodeMemReg64, IOpCodeSimd64 + { + public OpCodeSimdMemReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Size |= (opCode >> 21) & 4; + + Extend64 = false; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdMemSs64.cs b/ChocolArm64/Decoders/OpCodeSimdMemSs64.cs new file mode 100644 index 0000000000..07ec8ab730 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdMemSs64.cs @@ -0,0 +1,98 @@ +using ChocolArm64.Instructions; +using ChocolArm64.State; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdMemSs64 : OpCodeMemReg64, IOpCodeSimd64 + { + public int SElems { get; private set; } + public int Index { get; private set; } + public bool Replicate { get; private set; } + public bool WBack { get; private set; } + + public OpCodeSimdMemSs64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + int size = (opCode >> 10) & 3; + int s = (opCode >> 12) & 1; + int sElems = (opCode >> 12) & 2; + int scale = (opCode >> 14) & 3; + int l = (opCode >> 22) & 1; + int q = (opCode >> 30) & 1; + + sElems |= (opCode >> 21) & 1; + + sElems++; + + int index = (q << 3) | (s << 2) | size; + + switch (scale) + { + case 1: + { + if ((size & 1) != 0) + { + inst = Inst.Undefined; + + return; + } + + index >>= 1; + + break; + } + + case 2: + { + if ((size & 2) != 0 || + ((size & 1) != 0 && s != 0)) + { + inst = Inst.Undefined; + + return; + } + + if ((size & 1) != 0) + { + index >>= 3; + + scale = 3; + } + else + { + index >>= 2; + } + + break; + } + + case 3: + { + if (l == 0 || s != 0) + { + inst = Inst.Undefined; + + return; + } + + scale = size; + + Replicate = true; + + break; + } + } + + Index = index; + SElems = sElems; + Size = scale; + + Extend64 = false; + + WBack = ((opCode >> 23) & 1) != 0; + + RegisterSize = q != 0 + ? State.RegisterSize.Simd128 + : State.RegisterSize.Simd64; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdReg64.cs b/ChocolArm64/Decoders/OpCodeSimdReg64.cs new file mode 100644 index 0000000000..4bf462dee4 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdReg64.cs @@ -0,0 +1,18 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdReg64 : OpCodeSimd64 + { + public bool Bit3 { get; private set; } + public int Ra { get; private set; } + public int Rm { get; protected set; } + + public OpCodeSimdReg64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Bit3 = ((opCode >> 3) & 0x1) != 0; + Ra = (opCode >> 10) & 0x1f; + Rm = (opCode >> 16) & 0x1f; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdRegElem64.cs b/ChocolArm64/Decoders/OpCodeSimdRegElem64.cs new file mode 100644 index 0000000000..04e9586199 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdRegElem64.cs @@ -0,0 +1,31 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdRegElem64 : OpCodeSimdReg64 + { + public int Index { get; private set; } + + public OpCodeSimdRegElem64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + switch (Size) + { + case 1: + Index = (opCode >> 20) & 3 | + (opCode >> 9) & 4; + + Rm &= 0xf; + + break; + + case 2: + Index = (opCode >> 21) & 1 | + (opCode >> 10) & 2; + + break; + + default: Emitter = InstEmit.Und; return; + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSimdRegElemF64.cs b/ChocolArm64/Decoders/OpCodeSimdRegElemF64.cs new file mode 100644 index 0000000000..b5db7345ca --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdRegElemF64.cs @@ -0,0 +1,33 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdRegElemF64 : OpCodeSimdReg64 + { + public int Index { get; private set; } + + public OpCodeSimdRegElemF64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + switch ((opCode >> 21) & 3) // sz:L + { + case 0: // H:0 + Index = (opCode >> 10) & 2; // 0, 2 + + break; + + case 1: // H:1 + Index = (opCode >> 10) & 2; + Index++; // 1, 3 + + break; + + case 2: // H + Index = (opCode >> 11) & 1; // 0, 1 + + break; + + default: Emitter = InstEmit.Und; return; + } + } + } +} diff --git a/ChocolArm64/Decoders/OpCodeSimdShImm64.cs b/ChocolArm64/Decoders/OpCodeSimdShImm64.cs new file mode 100644 index 0000000000..d4cd88d13b --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdShImm64.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdShImm64 : OpCodeSimd64 + { + public int Imm { get; private set; } + + public OpCodeSimdShImm64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm = (opCode >> 16) & 0x7f; + + Size = BitUtils.HighestBitSetNibble(Imm >> 3); + } + } +} diff --git a/ChocolArm64/Decoders/OpCodeSimdTbl64.cs b/ChocolArm64/Decoders/OpCodeSimdTbl64.cs new file mode 100644 index 0000000000..13cc9090f5 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSimdTbl64.cs @@ -0,0 +1,12 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSimdTbl64 : OpCodeSimdReg64 + { + public OpCodeSimdTbl64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Size = ((opCode >> 13) & 3) + 1; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders/OpCodeSystem64.cs b/ChocolArm64/Decoders/OpCodeSystem64.cs new file mode 100644 index 0000000000..41c51565d9 --- /dev/null +++ b/ChocolArm64/Decoders/OpCodeSystem64.cs @@ -0,0 +1,24 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders +{ + class OpCodeSystem64 : OpCode64 + { + public int Rt { get; private set; } + public int Op2 { get; private set; } + public int CRm { get; private set; } + public int CRn { get; private set; } + public int Op1 { get; private set; } + public int Op0 { get; private set; } + + public OpCodeSystem64(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Rt = (opCode >> 0) & 0x1f; + Op2 = (opCode >> 5) & 0x7; + CRm = (opCode >> 8) & 0xf; + CRn = (opCode >> 12) & 0xf; + Op1 = (opCode >> 16) & 0x7; + Op0 = ((opCode >> 19) & 0x1) | 2; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoder/AShiftType.cs b/ChocolArm64/Decoders/ShiftType.cs similarity index 56% rename from ChocolArm64/Decoder/AShiftType.cs rename to ChocolArm64/Decoders/ShiftType.cs index 34ceea2087..5f6a7a4ca8 100644 --- a/ChocolArm64/Decoder/AShiftType.cs +++ b/ChocolArm64/Decoders/ShiftType.cs @@ -1,6 +1,6 @@ -namespace ChocolArm64.Decoder +namespace ChocolArm64.Decoders { - enum AShiftType + enum ShiftType { Lsl, Lsr, diff --git a/ChocolArm64/Decoders32/A32OpCode.cs b/ChocolArm64/Decoders32/A32OpCode.cs new file mode 100644 index 0000000000..f0177a4384 --- /dev/null +++ b/ChocolArm64/Decoders32/A32OpCode.cs @@ -0,0 +1,15 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders32 +{ + class A32OpCode : OpCode64 + { + public Cond Cond { get; private set; } + + public A32OpCode(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Cond = (Cond)((uint)opCode >> 28); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Decoders32/A32OpCodeBImmAl.cs b/ChocolArm64/Decoders32/A32OpCodeBImmAl.cs new file mode 100644 index 0000000000..c4a196b63e --- /dev/null +++ b/ChocolArm64/Decoders32/A32OpCodeBImmAl.cs @@ -0,0 +1,16 @@ +using ChocolArm64.Instructions; + +namespace ChocolArm64.Decoders32 +{ + class A32OpCodeBImmAl : A32OpCode + { + public int Imm; + public int H; + + public A32OpCodeBImmAl(Inst inst, long position, int opCode) : base(inst, position, opCode) + { + Imm = (opCode << 8) >> 6; + H = (opCode >> 23) & 2; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Events/ACpuTraceEventArgs.cs b/ChocolArm64/Events/ACpuTraceEventArgs.cs deleted file mode 100644 index fedf3865b1..0000000000 --- a/ChocolArm64/Events/ACpuTraceEventArgs.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace ChocolArm64.Events -{ - public class ACpuTraceEventArgs : EventArgs - { - public long Position { get; private set; } - - public string SubName { get; private set; } - - public ACpuTraceEventArgs(long Position, string SubName) - { - this.Position = Position; - this.SubName = SubName; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Events/AInstExceptionEventArgs.cs b/ChocolArm64/Events/AInstExceptionEventArgs.cs deleted file mode 100644 index a6853ea10e..0000000000 --- a/ChocolArm64/Events/AInstExceptionEventArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace ChocolArm64.Events -{ - public class AInstExceptionEventArgs : EventArgs - { - public long Position { get; private set; } - public int Id { get; private set; } - - public AInstExceptionEventArgs(long Position, int Id) - { - this.Position = Position; - this.Id = Id; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Events/AInstUndefinedEventArgs.cs b/ChocolArm64/Events/AInstUndefinedEventArgs.cs deleted file mode 100644 index cdc1728bd8..0000000000 --- a/ChocolArm64/Events/AInstUndefinedEventArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace ChocolArm64.Events -{ - public class AInstUndefinedEventArgs : EventArgs - { - public long Position { get; private set; } - public int RawOpCode { get; private set; } - - public AInstUndefinedEventArgs(long Position, int RawOpCode) - { - this.Position = Position; - this.RawOpCode = RawOpCode; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Events/CpuTraceEventArgs.cs b/ChocolArm64/Events/CpuTraceEventArgs.cs new file mode 100644 index 0000000000..c12781ed86 --- /dev/null +++ b/ChocolArm64/Events/CpuTraceEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace ChocolArm64.Events +{ + public class CpuTraceEventArgs : EventArgs + { + public long Position { get; private set; } + + public CpuTraceEventArgs(long position) + { + Position = position; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Events/InstExceptionEventArgs.cs b/ChocolArm64/Events/InstExceptionEventArgs.cs new file mode 100644 index 0000000000..e3cc0ba0e0 --- /dev/null +++ b/ChocolArm64/Events/InstExceptionEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace ChocolArm64.Events +{ + public class InstExceptionEventArgs : EventArgs + { + public long Position { get; private set; } + public int Id { get; private set; } + + public InstExceptionEventArgs(long position, int id) + { + Position = position; + Id = id; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Events/InstUndefinedEventArgs.cs b/ChocolArm64/Events/InstUndefinedEventArgs.cs new file mode 100644 index 0000000000..3ad7ea8b9e --- /dev/null +++ b/ChocolArm64/Events/InstUndefinedEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace ChocolArm64.Events +{ + public class InstUndefinedEventArgs : EventArgs + { + public long Position { get; private set; } + public int RawOpCode { get; private set; } + + public InstUndefinedEventArgs(long position, int rawOpCode) + { + Position = position; + RawOpCode = rawOpCode; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Events/InvalidAccessEventArgs.cs b/ChocolArm64/Events/InvalidAccessEventArgs.cs new file mode 100644 index 0000000000..a8046d7375 --- /dev/null +++ b/ChocolArm64/Events/InvalidAccessEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace ChocolArm64.Events +{ + public class InvalidAccessEventArgs : EventArgs + { + public long Position { get; private set; } + + public InvalidAccessEventArgs(long position) + { + Position = position; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs b/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs index 4a03b65cb9..d6ddf75227 100644 --- a/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs +++ b/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs @@ -8,6 +8,6 @@ namespace ChocolArm64.Exceptions public VmmAccessException() { } - public VmmAccessException(long Position, long Size) : base(string.Format(ExMsg, Position, Size)) { } + public VmmAccessException(long position, long size) : base(string.Format(ExMsg, position, size)) { } } } \ No newline at end of file diff --git a/ChocolArm64/Exceptions/VmmPageFaultException.cs b/ChocolArm64/Exceptions/VmmPageFaultException.cs index d55c2c1ca9..f33aafc013 100644 --- a/ChocolArm64/Exceptions/VmmPageFaultException.cs +++ b/ChocolArm64/Exceptions/VmmPageFaultException.cs @@ -8,6 +8,6 @@ namespace ChocolArm64.Exceptions public VmmPageFaultException() { } - public VmmPageFaultException(long Position) : base(string.Format(ExMsg, Position)) { } + public VmmPageFaultException(long position) : base(string.Format(ExMsg, position)) { } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInst.cs b/ChocolArm64/Instruction/AInst.cs deleted file mode 100644 index 7409353614..0000000000 --- a/ChocolArm64/Instruction/AInst.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace ChocolArm64.Instruction -{ - struct AInst - { - public AInstInterpreter Interpreter { get; private set; } - public AInstEmitter Emitter { get; private set; } - public Type Type { get; private set; } - - public static AInst Undefined => new AInst(null, AInstEmit.Und, null); - - public AInst(AInstInterpreter Interpreter, AInstEmitter Emitter, Type Type) - { - this.Interpreter = Interpreter; - this.Emitter = Emitter; - this.Type = Type; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitAlu.cs b/ChocolArm64/Instruction/AInstEmitAlu.cs deleted file mode 100644 index 490387e129..0000000000 --- a/ChocolArm64/Instruction/AInstEmitAlu.cs +++ /dev/null @@ -1,392 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; - -using static ChocolArm64.Instruction.AInstEmitAluHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Adc(AILEmitterCtx Context) => EmitAdc(Context, false); - public static void Adcs(AILEmitterCtx Context) => EmitAdc(Context, true); - - private static void EmitAdc(AILEmitterCtx Context, bool SetFlags) - { - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Add); - - Context.EmitLdflg((int)APState.CBit); - - Type[] MthdTypes = new Type[] { typeof(bool) }; - - MethodInfo MthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), MthdTypes); - - Context.EmitCall(MthdInfo); - - if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.Emit(OpCodes.Add); - - if (SetFlags) - { - Context.EmitZNFlagCheck(); - - EmitAdcsCCheck(Context); - EmitAddsVCheck(Context); - } - - EmitDataStore(Context); - } - - public static void Add(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Add); - - public static void Adds(AILEmitterCtx Context) - { - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Add); - - Context.EmitZNFlagCheck(); - - EmitAddsCCheck(Context); - EmitAddsVCheck(Context); - EmitDataStoreS(Context); - } - - public static void And(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.And); - - public static void Ands(AILEmitterCtx Context) - { - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.And); - - EmitZeroCVFlags(Context); - - Context.EmitZNFlagCheck(); - - EmitDataStoreS(Context); - } - - public static void Asrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr); - - public static void Bic(AILEmitterCtx Context) => EmitBic(Context, false); - public static void Bics(AILEmitterCtx Context) => EmitBic(Context, true); - - private static void EmitBic(AILEmitterCtx Context, bool SetFlags) - { - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.And); - - if (SetFlags) - { - EmitZeroCVFlags(Context); - - Context.EmitZNFlagCheck(); - } - - EmitDataStore(Context, SetFlags); - } - - public static void Cls(AILEmitterCtx Context) - { - AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - Context.EmitLdc_I4(Op.RegisterSize == ARegisterSize.Int32 ? 32 : 64); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns)); - - Context.EmitStintzr(Op.Rd); - } - - public static void Clz(AILEmitterCtx Context) - { - AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - Context.EmitLdc_I4(Op.RegisterSize == ARegisterSize.Int32 ? 32 : 64); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros)); - - Context.EmitStintzr(Op.Rd); - } - - public static void Eon(AILEmitterCtx Context) - { - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.Xor); - - EmitDataStore(Context); - } - - public static void Eor(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Xor); - - public static void Extr(AILEmitterCtx Context) - { - //TODO: Ensure that the Shift is valid for the Is64Bits. - AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; - - Context.EmitLdintzr(Op.Rm); - - if (Op.Shift > 0) - { - Context.EmitLdc_I4(Op.Shift); - - Context.Emit(OpCodes.Shr_Un); - - Context.EmitLdintzr(Op.Rn); - Context.EmitLdc_I4(Op.GetBitsCount() - Op.Shift); - - Context.Emit(OpCodes.Shl); - Context.Emit(OpCodes.Or); - } - - EmitDataStore(Context); - } - - public static void Lslv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shl); - public static void Lsrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr_Un); - - public static void Sbc(AILEmitterCtx Context) => EmitSbc(Context, false); - public static void Sbcs(AILEmitterCtx Context) => EmitSbc(Context, true); - - private static void EmitSbc(AILEmitterCtx Context, bool SetFlags) - { - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Sub); - - Context.EmitLdflg((int)APState.CBit); - - Type[] MthdTypes = new Type[] { typeof(bool) }; - - MethodInfo MthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), MthdTypes); - - Context.EmitCall(MthdInfo); - - Context.EmitLdc_I4(1); - - Context.Emit(OpCodes.Xor); - - if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.Emit(OpCodes.Sub); - - if (SetFlags) - { - Context.EmitZNFlagCheck(); - - EmitSbcsCCheck(Context); - EmitSubsVCheck(Context); - } - - EmitDataStore(Context); - } - - public static void Sub(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Sub); - - public static void Subs(AILEmitterCtx Context) - { - Context.TryOptMarkCondWithoutCmp(); - - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Sub); - - Context.EmitZNFlagCheck(); - - EmitSubsCCheck(Context); - EmitSubsVCheck(Context); - EmitDataStoreS(Context); - } - - public static void Orn(AILEmitterCtx Context) - { - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.Or); - - EmitDataStore(Context); - } - - public static void Orr(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Or); - - public static void Rbit(AILEmitterCtx Context) => EmitFallback32_64(Context, - nameof(ASoftFallback.ReverseBits32), - nameof(ASoftFallback.ReverseBits64)); - - public static void Rev16(AILEmitterCtx Context) => EmitFallback32_64(Context, - nameof(ASoftFallback.ReverseBytes16_32), - nameof(ASoftFallback.ReverseBytes16_64)); - - public static void Rev32(AILEmitterCtx Context) => EmitFallback32_64(Context, - nameof(ASoftFallback.ReverseBytes32_32), - nameof(ASoftFallback.ReverseBytes32_64)); - - private static void EmitFallback32_64(AILEmitterCtx Context, string Name32, string Name64) - { - AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - if (Op.RegisterSize == ARegisterSize.Int32) - { - ASoftFallback.EmitCall(Context, Name32); - } - else - { - ASoftFallback.EmitCall(Context, Name64); - } - - Context.EmitStintzr(Op.Rd); - } - - public static void Rev64(AILEmitterCtx Context) - { - AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBytes64)); - - Context.EmitStintzr(Op.Rd); - } - - public static void Rorv(AILEmitterCtx Context) - { - EmitDataLoadRn(Context); - EmitDataLoadShift(Context); - - Context.Emit(OpCodes.Shr_Un); - - EmitDataLoadRn(Context); - - Context.EmitLdc_I4(Context.CurrOp.GetBitsCount()); - - EmitDataLoadShift(Context); - - Context.Emit(OpCodes.Sub); - Context.Emit(OpCodes.Shl); - Context.Emit(OpCodes.Or); - - EmitDataStore(Context); - } - - public static void Sdiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div); - public static void Udiv(AILEmitterCtx Context) => EmitDiv(Context, OpCodes.Div_Un); - - private static void EmitDiv(AILEmitterCtx Context, OpCode ILOp) - { - //If Rm == 0, Rd = 0 (division by zero). - Context.EmitLdc_I(0); - - EmitDataLoadRm(Context); - - Context.EmitLdc_I(0); - - AILLabel BadDiv = new AILLabel(); - - Context.Emit(OpCodes.Beq_S, BadDiv); - Context.Emit(OpCodes.Pop); - - if (ILOp == OpCodes.Div) - { - //If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow). - long IntMin = 1L << (Context.CurrOp.GetBitsCount() - 1); - - Context.EmitLdc_I(IntMin); - - EmitDataLoadRn(Context); - - Context.EmitLdc_I(IntMin); - - Context.Emit(OpCodes.Ceq); - - EmitDataLoadRm(Context); - - Context.EmitLdc_I(-1); - - Context.Emit(OpCodes.Ceq); - Context.Emit(OpCodes.And); - Context.Emit(OpCodes.Brtrue_S, BadDiv); - Context.Emit(OpCodes.Pop); - } - - EmitDataLoadRn(Context); - EmitDataLoadRm(Context); - - Context.Emit(ILOp); - - Context.MarkLabel(BadDiv); - - EmitDataStore(Context); - } - - private static void EmitDataOp(AILEmitterCtx Context, OpCode ILOp) - { - EmitDataLoadOpers(Context); - - Context.Emit(ILOp); - - EmitDataStore(Context); - } - - private static void EmitDataOpShift(AILEmitterCtx Context, OpCode ILOp) - { - EmitDataLoadRn(Context); - EmitDataLoadShift(Context); - - Context.Emit(ILOp); - - EmitDataStore(Context); - } - - private static void EmitDataLoadShift(AILEmitterCtx Context) - { - EmitDataLoadRm(Context); - - Context.EmitLdc_I(Context.CurrOp.GetBitsCount() - 1); - - Context.Emit(OpCodes.And); - - //Note: Only 32-bits shift values are valid, so when the value is 64-bits - //we need to cast it to a 32-bits integer. This is fine because we - //AND the value and only keep the lower 5 or 6 bits anyway -- it - //could very well fit on a byte. - if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_I4); - } - } - - private static void EmitZeroCVFlags(AILEmitterCtx Context) - { - Context.EmitLdc_I4(0); - - Context.EmitStflg((int)APState.VBit); - - Context.EmitLdc_I4(0); - - Context.EmitStflg((int)APState.CBit); - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitAluHelper.cs b/ChocolArm64/Instruction/AInstEmitAluHelper.cs deleted file mode 100644 index ef9dd7a7a5..0000000000 --- a/ChocolArm64/Instruction/AInstEmitAluHelper.cs +++ /dev/null @@ -1,212 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static class AInstEmitAluHelper - { - public static void EmitAdcsCCheck(AILEmitterCtx Context) - { - //C = (Rd == Rn && CIn) || Rd < Rn - Context.EmitSttmp(); - Context.EmitLdtmp(); - Context.EmitLdtmp(); - - EmitDataLoadRn(Context); - - Context.Emit(OpCodes.Ceq); - - Context.EmitLdflg((int)APState.CBit); - - Context.Emit(OpCodes.And); - - Context.EmitLdtmp(); - - EmitDataLoadRn(Context); - - Context.Emit(OpCodes.Clt_Un); - Context.Emit(OpCodes.Or); - - Context.EmitStflg((int)APState.CBit); - } - - public static void EmitAddsCCheck(AILEmitterCtx Context) - { - //C = Rd < Rn - Context.Emit(OpCodes.Dup); - - EmitDataLoadRn(Context); - - Context.Emit(OpCodes.Clt_Un); - - Context.EmitStflg((int)APState.CBit); - } - - public static void EmitAddsVCheck(AILEmitterCtx Context) - { - //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 - Context.Emit(OpCodes.Dup); - - EmitDataLoadRn(Context); - - Context.Emit(OpCodes.Xor); - - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Xor); - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.And); - - Context.EmitLdc_I(0); - - Context.Emit(OpCodes.Clt); - - Context.EmitStflg((int)APState.VBit); - } - - public static void EmitSbcsCCheck(AILEmitterCtx Context) - { - //C = (Rn == Rm && CIn) || Rn > Rm - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Ceq); - - Context.EmitLdflg((int)APState.CBit); - - Context.Emit(OpCodes.And); - - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Cgt_Un); - Context.Emit(OpCodes.Or); - - Context.EmitStflg((int)APState.CBit); - } - - public static void EmitSubsCCheck(AILEmitterCtx Context) - { - //C = Rn == Rm || Rn > Rm = !(Rn < Rm) - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Clt_Un); - - Context.EmitLdc_I4(1); - - Context.Emit(OpCodes.Xor); - - Context.EmitStflg((int)APState.CBit); - } - - public static void EmitSubsVCheck(AILEmitterCtx Context) - { - //V = (Rd ^ Rn) & (Rn ^ Rm) < 0 - Context.Emit(OpCodes.Dup); - - EmitDataLoadRn(Context); - - Context.Emit(OpCodes.Xor); - - EmitDataLoadOpers(Context); - - Context.Emit(OpCodes.Xor); - Context.Emit(OpCodes.And); - - Context.EmitLdc_I(0); - - Context.Emit(OpCodes.Clt); - - Context.EmitStflg((int)APState.VBit); - } - - public static void EmitDataLoadRm(AILEmitterCtx Context) - { - Context.EmitLdintzr(((IAOpCodeAluRs)Context.CurrOp).Rm); - } - - public static void EmitDataLoadOpers(AILEmitterCtx Context) - { - EmitDataLoadRn(Context); - EmitDataLoadOper2(Context); - } - - public static void EmitDataLoadRn(AILEmitterCtx Context) - { - IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp; - - if (Op.DataOp == ADataOp.Logical || Op is IAOpCodeAluRs) - { - Context.EmitLdintzr(Op.Rn); - } - else - { - Context.EmitLdint(Op.Rn); - } - } - - public static void EmitDataLoadOper2(AILEmitterCtx Context) - { - switch (Context.CurrOp) - { - case IAOpCodeAluImm Op: - Context.EmitLdc_I(Op.Imm); - break; - - case IAOpCodeAluRs Op: - Context.EmitLdintzr(Op.Rm); - - switch (Op.ShiftType) - { - case AShiftType.Lsl: Context.EmitLsl(Op.Shift); break; - case AShiftType.Lsr: Context.EmitLsr(Op.Shift); break; - case AShiftType.Asr: Context.EmitAsr(Op.Shift); break; - case AShiftType.Ror: Context.EmitRor(Op.Shift); break; - } - break; - - case IAOpCodeAluRx Op: - Context.EmitLdintzr(Op.Rm); - Context.EmitCast(Op.IntType); - Context.EmitLsl(Op.Shift); - break; - } - } - - public static void EmitDataStore(AILEmitterCtx Context) => EmitDataStore(Context, false); - public static void EmitDataStoreS(AILEmitterCtx Context) => EmitDataStore(Context, true); - - public static void EmitDataStore(AILEmitterCtx Context, bool SetFlags) - { - IAOpCodeAlu Op = (IAOpCodeAlu)Context.CurrOp; - - if (SetFlags || Op is IAOpCodeAluRs) - { - Context.EmitStintzr(Op.Rd); - } - else - { - Context.EmitStint(Op.Rd); - } - } - - public static void EmitSetNZCV(AILEmitterCtx Context, int NZCV) - { - Context.EmitLdc_I4((NZCV >> 0) & 1); - - Context.EmitStflg((int)APState.VBit); - - Context.EmitLdc_I4((NZCV >> 1) & 1); - - Context.EmitStflg((int)APState.CBit); - - Context.EmitLdc_I4((NZCV >> 2) & 1); - - Context.EmitStflg((int)APState.ZBit); - - Context.EmitLdc_I4((NZCV >> 3) & 1); - - Context.EmitStflg((int)APState.NBit); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitBfm.cs b/ChocolArm64/Instruction/AInstEmitBfm.cs deleted file mode 100644 index 2e8f250858..0000000000 --- a/ChocolArm64/Instruction/AInstEmitBfm.cs +++ /dev/null @@ -1,208 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Bfm(AILEmitterCtx Context) - { - AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - - EmitBfmLoadRn(Context); - - Context.EmitLdintzr(Op.Rd); - Context.EmitLdc_I(~Op.WMask & Op.TMask); - - Context.Emit(OpCodes.And); - Context.Emit(OpCodes.Or); - - Context.EmitLdintzr(Op.Rd); - Context.EmitLdc_I(~Op.TMask); - - Context.Emit(OpCodes.And); - Context.Emit(OpCodes.Or); - - Context.EmitStintzr(Op.Rd); - } - - public static void Sbfm(AILEmitterCtx Context) - { - AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - - int BitsCount = Op.GetBitsCount(); - - if (Op.Pos + 1 == BitsCount) - { - EmitSbfmShift(Context); - } - else if (Op.Pos < Op.Shift) - { - EmitSbfiz(Context); - } - else if (Op.Pos == 7 && Op.Shift == 0) - { - EmitSbfmCast(Context, OpCodes.Conv_I1); - } - else if (Op.Pos == 15 && Op.Shift == 0) - { - EmitSbfmCast(Context, OpCodes.Conv_I2); - } - else if (Op.Pos == 31 && Op.Shift == 0) - { - EmitSbfmCast(Context, OpCodes.Conv_I4); - } - else - { - EmitBfmLoadRn(Context); - - Context.EmitLdintzr(Op.Rn); - - Context.EmitLsl(BitsCount - 1 - Op.Pos); - Context.EmitAsr(BitsCount - 1); - - Context.EmitLdc_I(~Op.TMask); - - Context.Emit(OpCodes.And); - Context.Emit(OpCodes.Or); - - Context.EmitStintzr(Op.Rd); - } - } - - public static void Ubfm(AILEmitterCtx Context) - { - AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - - if (Op.Pos + 1 == Op.GetBitsCount()) - { - EmitUbfmShift(Context); - } - else if (Op.Pos < Op.Shift) - { - EmitUbfiz(Context); - } - else if (Op.Pos + 1 == Op.Shift) - { - EmitBfmLsl(Context); - } - else if (Op.Pos == 7 && Op.Shift == 0) - { - EmitUbfmCast(Context, OpCodes.Conv_U1); - } - else if (Op.Pos == 15 && Op.Shift == 0) - { - EmitUbfmCast(Context, OpCodes.Conv_U2); - } - else - { - EmitBfmLoadRn(Context); - - Context.EmitStintzr(Op.Rd); - } - } - - private static void EmitSbfiz(AILEmitterCtx Context) => EmitBfiz(Context, true); - private static void EmitUbfiz(AILEmitterCtx Context) => EmitBfiz(Context, false); - - private static void EmitBfiz(AILEmitterCtx Context, bool Signed) - { - AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - - int Width = Op.Pos + 1; - - Context.EmitLdintzr(Op.Rn); - - Context.EmitLsl(Op.GetBitsCount() - Width); - - if (Signed) - { - Context.EmitAsr(Op.Shift - Width); - } - else - { - Context.EmitLsr(Op.Shift - Width); - } - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitSbfmCast(AILEmitterCtx Context, OpCode ILOp) - { - EmitBfmCast(Context, ILOp, true); - } - - private static void EmitUbfmCast(AILEmitterCtx Context, OpCode ILOp) - { - EmitBfmCast(Context, ILOp, false); - } - - private static void EmitBfmCast(AILEmitterCtx Context, OpCode ILOp, bool Signed) - { - AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - Context.Emit(ILOp); - - if (Op.RegisterSize != ARegisterSize.Int32) - { - Context.Emit(Signed - ? OpCodes.Conv_I8 - : OpCodes.Conv_U8); - } - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitSbfmShift(AILEmitterCtx Context) - { - EmitBfmShift(Context, true); - } - - private static void EmitUbfmShift(AILEmitterCtx Context) - { - EmitBfmShift(Context, false); - } - - private static void EmitBfmShift(AILEmitterCtx Context, bool Signed) - { - AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - Context.EmitLdc_I4(Op.Shift); - - Context.Emit(Signed - ? OpCodes.Shr - : OpCodes.Shr_Un); - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitBfmLsl(AILEmitterCtx Context) - { - AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - Context.EmitLsl(Op.GetBitsCount() - Op.Shift); - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitBfmLoadRn(AILEmitterCtx Context) - { - AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - Context.EmitRor(Op.Shift); - - Context.EmitLdc_I(Op.WMask & Op.TMask); - - Context.Emit(OpCodes.And); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitCcmp.cs b/ChocolArm64/Instruction/AInstEmitCcmp.cs deleted file mode 100644 index 7153a6a0d1..0000000000 --- a/ChocolArm64/Instruction/AInstEmitCcmp.cs +++ /dev/null @@ -1,81 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; - -using static ChocolArm64.Instruction.AInstEmitAluHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - private enum CcmpOp - { - Cmp, - Cmn - } - - public static void Ccmn(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmn); - public static void Ccmp(AILEmitterCtx Context) => EmitCcmp(Context, CcmpOp.Cmp); - - private static void EmitCcmp(AILEmitterCtx Context, CcmpOp CmpOp) - { - AOpCodeCcmp Op = (AOpCodeCcmp)Context.CurrOp; - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.EmitCondBranch(LblTrue, Op.Cond); - - Context.EmitLdc_I4((Op.NZCV >> 0) & 1); - - Context.EmitStflg((int)APState.VBit); - - Context.EmitLdc_I4((Op.NZCV >> 1) & 1); - - Context.EmitStflg((int)APState.CBit); - - Context.EmitLdc_I4((Op.NZCV >> 2) & 1); - - Context.EmitStflg((int)APState.ZBit); - - Context.EmitLdc_I4((Op.NZCV >> 3) & 1); - - Context.EmitStflg((int)APState.NBit); - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - EmitDataLoadOpers(Context); - - if (CmpOp == CcmpOp.Cmp) - { - Context.Emit(OpCodes.Sub); - - Context.EmitZNFlagCheck(); - - EmitSubsCCheck(Context); - EmitSubsVCheck(Context); - } - else if (CmpOp == CcmpOp.Cmn) - { - Context.Emit(OpCodes.Add); - - Context.EmitZNFlagCheck(); - - EmitAddsCCheck(Context); - EmitAddsVCheck(Context); - } - else - { - throw new ArgumentException(nameof(CmpOp)); - } - - Context.Emit(OpCodes.Pop); - - Context.MarkLabel(LblEnd); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitCsel.cs b/ChocolArm64/Instruction/AInstEmitCsel.cs deleted file mode 100644 index 218767524e..0000000000 --- a/ChocolArm64/Instruction/AInstEmitCsel.cs +++ /dev/null @@ -1,58 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - private enum CselOperation - { - None, - Increment, - Invert, - Negate - } - - public static void Csel(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.None); - public static void Csinc(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Increment); - public static void Csinv(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Invert); - public static void Csneg(AILEmitterCtx Context) => EmitCsel(Context, CselOperation.Negate); - - private static void EmitCsel(AILEmitterCtx Context, CselOperation CselOp) - { - AOpCodeCsel Op = (AOpCodeCsel)Context.CurrOp; - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.EmitCondBranch(LblTrue, Op.Cond); - Context.EmitLdintzr(Op.Rm); - - if (CselOp == CselOperation.Increment) - { - Context.EmitLdc_I(1); - - Context.Emit(OpCodes.Add); - } - else if (CselOp == CselOperation.Invert) - { - Context.Emit(OpCodes.Not); - } - else if (CselOp == CselOperation.Negate) - { - Context.Emit(OpCodes.Neg); - } - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - Context.EmitLdintzr(Op.Rn); - - Context.MarkLabel(LblEnd); - - Context.EmitStintzr(Op.Rd); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs deleted file mode 100644 index 73d2096732..0000000000 --- a/ChocolArm64/Instruction/AInstEmitException.cs +++ /dev/null @@ -1,86 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Brk(AILEmitterCtx Context) - { - EmitExceptionCall(Context, nameof(AThreadState.OnBreak)); - } - - public static void Svc(AILEmitterCtx Context) - { - EmitExceptionCall(Context, nameof(AThreadState.OnSvcCall)); - } - - private static void EmitExceptionCall(AILEmitterCtx Context, string MthdName) - { - AOpCodeException Op = (AOpCodeException)Context.CurrOp; - - Context.EmitStoreState(); - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitLdc_I8(Op.Position); - Context.EmitLdc_I4(Op.Id); - - Context.EmitPrivateCall(typeof(AThreadState), MthdName); - - //Check if the thread should still be running, if it isn't then we return 0 - //to force a return to the dispatcher and then exit the thread. - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Running)); - - AILLabel LblEnd = new AILLabel(); - - Context.Emit(OpCodes.Brtrue_S, LblEnd); - - Context.EmitLdc_I8(0); - - Context.Emit(OpCodes.Ret); - - Context.MarkLabel(LblEnd); - - if (Context.CurrBlock.Next != null) - { - Context.EmitLoadState(Context.CurrBlock.Next); - } - else - { - Context.EmitLdc_I8(Op.Position + 4); - - Context.Emit(OpCodes.Ret); - } - } - - public static void Und(AILEmitterCtx Context) - { - AOpCode Op = Context.CurrOp; - - Context.EmitStoreState(); - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitLdc_I8(Op.Position); - Context.EmitLdc_I4(Op.RawOpCode); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.OnUndefined)); - - if (Context.CurrBlock.Next != null) - { - Context.EmitLoadState(Context.CurrBlock.Next); - } - else - { - Context.EmitLdc_I8(Op.Position + 4); - - Context.Emit(OpCodes.Ret); - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitFlow.cs b/ChocolArm64/Instruction/AInstEmitFlow.cs deleted file mode 100644 index 89979d0509..0000000000 --- a/ChocolArm64/Instruction/AInstEmitFlow.cs +++ /dev/null @@ -1,220 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void B(AILEmitterCtx Context) - { - AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; - - if (Context.CurrBlock.Branch != null) - { - Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm)); - } - else - { - Context.EmitStoreState(); - Context.EmitLdc_I8(Op.Imm); - - Context.Emit(OpCodes.Ret); - } - } - - public static void B_Cond(AILEmitterCtx Context) - { - AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp; - - EmitBranch(Context, Op.Cond); - } - - public static void Bl(AILEmitterCtx Context) - { - AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; - - if (AOptimizations.GenerateCallStack) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitLdc_I8(Op.Imm); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod)); - } - - Context.EmitLdc_I(Op.Position + 4); - Context.EmitStint(AThreadState.LRIndex); - Context.EmitStoreState(); - - if (Context.TryOptEmitSubroutineCall()) - { - //Note: the return value of the called method will be placed - //at the Stack, the return value is always a Int64 with the - //return address of the function. We check if the address is - //correct, if it isn't we keep returning until we reach the dispatcher. - Context.Emit(OpCodes.Dup); - - Context.EmitLdc_I8(Op.Position + 4); - - AILLabel LblContinue = new AILLabel(); - - Context.Emit(OpCodes.Beq_S, LblContinue); - Context.Emit(OpCodes.Ret); - - Context.MarkLabel(LblContinue); - - Context.Emit(OpCodes.Pop); - - Context.EmitLoadState(Context.CurrBlock.Next); - } - else - { - Context.EmitLdc_I8(Op.Imm); - - Context.Emit(OpCodes.Ret); - } - } - - public static void Blr(AILEmitterCtx Context) - { - AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; - - if (AOptimizations.GenerateCallStack) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitLdintzr(Op.Rn); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod)); - } - - Context.EmitLdc_I(Op.Position + 4); - Context.EmitStint(AThreadState.LRIndex); - Context.EmitStoreState(); - Context.EmitLdintzr(Op.Rn); - - Context.Emit(OpCodes.Ret); - } - - public static void Br(AILEmitterCtx Context) - { - AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; - - if (AOptimizations.GenerateCallStack) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitLdintzr(Op.Rn); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.JumpMethod)); - } - - Context.EmitStoreState(); - Context.EmitLdintzr(Op.Rn); - - Context.Emit(OpCodes.Ret); - } - - public static void Cbnz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Bne_Un); - public static void Cbz(AILEmitterCtx Context) => EmitCb(Context, OpCodes.Beq); - - private static void EmitCb(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeBImmCmp Op = (AOpCodeBImmCmp)Context.CurrOp; - - Context.EmitLdintzr(Op.Rt); - Context.EmitLdc_I(0); - - EmitBranch(Context, ILOp); - } - - public static void Ret(AILEmitterCtx Context) - { - if (AOptimizations.GenerateCallStack) - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.ExitMethod)); - } - - Context.EmitStoreState(); - Context.EmitLdint(AThreadState.LRIndex); - - Context.Emit(OpCodes.Ret); - } - - public static void Tbnz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Bne_Un); - public static void Tbz(AILEmitterCtx Context) => EmitTb(Context, OpCodes.Beq); - - private static void EmitTb(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeBImmTest Op = (AOpCodeBImmTest)Context.CurrOp; - - Context.EmitLdintzr(Op.Rt); - Context.EmitLdc_I(1L << Op.Pos); - - Context.Emit(OpCodes.And); - - Context.EmitLdc_I(0); - - EmitBranch(Context, ILOp); - } - - private static void EmitBranch(AILEmitterCtx Context, ACond Cond) - { - AOpCodeBImm Op = (AOpCodeBImm)Context.CurrOp; - - if (Context.CurrBlock.Next != null && - Context.CurrBlock.Branch != null) - { - Context.EmitCondBranch(Context.GetLabel(Op.Imm), Cond); - } - else - { - Context.EmitStoreState(); - - AILLabel LblTaken = new AILLabel(); - - Context.EmitCondBranch(LblTaken, Cond); - - Context.EmitLdc_I8(Op.Position + 4); - - Context.Emit(OpCodes.Ret); - - Context.MarkLabel(LblTaken); - - Context.EmitLdc_I8(Op.Imm); - - Context.Emit(OpCodes.Ret); - } - } - - private static void EmitBranch(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeBImm Op = (AOpCodeBImm)Context.CurrOp; - - if (Context.CurrBlock.Next != null && - Context.CurrBlock.Branch != null) - { - Context.Emit(ILOp, Context.GetLabel(Op.Imm)); - } - else - { - Context.EmitStoreState(); - - AILLabel LblTaken = new AILLabel(); - - Context.Emit(ILOp, LblTaken); - - Context.EmitLdc_I8(Op.Position + 4); - - Context.Emit(OpCodes.Ret); - - Context.MarkLabel(LblTaken); - - Context.EmitLdc_I8(Op.Imm); - - Context.Emit(OpCodes.Ret); - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitHash.cs b/ChocolArm64/Instruction/AInstEmitHash.cs deleted file mode 100644 index 69bdbc480d..0000000000 --- a/ChocolArm64/Instruction/AInstEmitHash.cs +++ /dev/null @@ -1,115 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Crc32b(AILEmitterCtx Context) - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32b)); - } - - public static void Crc32h(AILEmitterCtx Context) - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32h)); - } - - public static void Crc32w(AILEmitterCtx Context) - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32w)); - } - - public static void Crc32x(AILEmitterCtx Context) - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32x)); - } - - public static void Crc32cb(AILEmitterCtx Context) - { - if (AOptimizations.UseSse42) - { - EmitSse42Crc32(Context, typeof(uint), typeof(byte)); - } - else - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32cb)); - } - } - - public static void Crc32ch(AILEmitterCtx Context) - { - if (AOptimizations.UseSse42) - { - EmitSse42Crc32(Context, typeof(uint), typeof(ushort)); - } - else - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32ch)); - } - } - - public static void Crc32cw(AILEmitterCtx Context) - { - if (AOptimizations.UseSse42) - { - EmitSse42Crc32(Context, typeof(uint), typeof(uint)); - } - else - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32cw)); - } - } - - public static void Crc32cx(AILEmitterCtx Context) - { - if (AOptimizations.UseSse42) - { - EmitSse42Crc32(Context, typeof(ulong), typeof(ulong)); - } - else - { - EmitCrc32(Context, nameof(ASoftFallback.Crc32cx)); - } - } - - private static void EmitSse42Crc32(AILEmitterCtx Context, Type TCrc, Type TData) - { - AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - Context.EmitLdintzr(Op.Rm); - - Context.EmitCall(typeof(Sse42).GetMethod(nameof(Sse42.Crc32), new Type[] { TCrc, TData })); - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitCrc32(AILEmitterCtx Context, string Name) - { - AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - if (Op.RegisterSize != ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U4); - } - - Context.EmitLdintzr(Op.Rm); - - ASoftFallback.EmitCall(Context, Name); - - if (Op.RegisterSize != ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.EmitStintzr(Op.Rd); - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitMemory.cs b/ChocolArm64/Instruction/AInstEmitMemory.cs deleted file mode 100644 index 67653ed06c..0000000000 --- a/ChocolArm64/Instruction/AInstEmitMemory.cs +++ /dev/null @@ -1,252 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -using static ChocolArm64.Instruction.AInstEmitMemoryHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Adr(AILEmitterCtx Context) - { - AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp; - - Context.EmitLdc_I(Op.Position + Op.Imm); - Context.EmitStintzr(Op.Rd); - } - - public static void Adrp(AILEmitterCtx Context) - { - AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp; - - Context.EmitLdc_I((Op.Position & ~0xfffL) + (Op.Imm << 12)); - Context.EmitStintzr(Op.Rd); - } - - public static void Ldr(AILEmitterCtx Context) => EmitLdr(Context, false); - public static void Ldrs(AILEmitterCtx Context) => EmitLdr(Context, true); - - private static void EmitLdr(AILEmitterCtx Context, bool Signed) - { - AOpCodeMem Op = (AOpCodeMem)Context.CurrOp; - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - - EmitLoadAddress(Context); - - if (Signed && Op.Extend64) - { - EmitReadSx64Call(Context, Op.Size); - } - else if (Signed) - { - EmitReadSx32Call(Context, Op.Size); - } - else - { - EmitReadZxCall(Context, Op.Size); - } - - if (Op is IAOpCodeSimd) - { - Context.EmitStvec(Op.Rt); - } - else - { - Context.EmitStintzr(Op.Rt); - } - - EmitWBackIfNeeded(Context); - } - - public static void LdrLit(AILEmitterCtx Context) - { - IAOpCodeLit Op = (IAOpCodeLit)Context.CurrOp; - - if (Op.Prefetch) - { - return; - } - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdc_I8(Op.Imm); - - if (Op.Signed) - { - EmitReadSx64Call(Context, Op.Size); - } - else - { - EmitReadZxCall(Context, Op.Size); - } - - if (Op is IAOpCodeSimd) - { - Context.EmitStvec(Op.Rt); - } - else - { - Context.EmitStint(Op.Rt); - } - } - - public static void Ldp(AILEmitterCtx Context) - { - AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp; - - void EmitReadAndStore(int Rt) - { - if (Op.Extend64) - { - EmitReadSx64Call(Context, Op.Size); - } - else - { - EmitReadZxCall(Context, Op.Size); - } - - if (Op is IAOpCodeSimd) - { - Context.EmitStvec(Rt); - } - else - { - Context.EmitStintzr(Rt); - } - } - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - - EmitLoadAddress(Context); - - EmitReadAndStore(Op.Rt); - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdtmp(); - Context.EmitLdc_I8(1 << Op.Size); - - Context.Emit(OpCodes.Add); - - EmitReadAndStore(Op.Rt2); - - EmitWBackIfNeeded(Context); - } - - public static void Str(AILEmitterCtx Context) - { - AOpCodeMem Op = (AOpCodeMem)Context.CurrOp; - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - - EmitLoadAddress(Context); - - if (Op is IAOpCodeSimd) - { - Context.EmitLdvec(Op.Rt); - } - else - { - Context.EmitLdintzr(Op.Rt); - } - - EmitWriteCall(Context, Op.Size); - - EmitWBackIfNeeded(Context); - } - - public static void Stp(AILEmitterCtx Context) - { - AOpCodeMemPair Op = (AOpCodeMemPair)Context.CurrOp; - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - - EmitLoadAddress(Context); - - if (Op is IAOpCodeSimd) - { - Context.EmitLdvec(Op.Rt); - } - else - { - Context.EmitLdintzr(Op.Rt); - } - - EmitWriteCall(Context, Op.Size); - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdtmp(); - Context.EmitLdc_I8(1 << Op.Size); - - Context.Emit(OpCodes.Add); - - if (Op is IAOpCodeSimd) - { - Context.EmitLdvec(Op.Rt2); - } - else - { - Context.EmitLdintzr(Op.Rt2); - } - - EmitWriteCall(Context, Op.Size); - - EmitWBackIfNeeded(Context); - } - - private static void EmitLoadAddress(AILEmitterCtx Context) - { - switch (Context.CurrOp) - { - case AOpCodeMemImm Op: - Context.EmitLdint(Op.Rn); - - if (!Op.PostIdx) - { - //Pre-indexing. - Context.EmitLdc_I(Op.Imm); - - Context.Emit(OpCodes.Add); - } - break; - - case AOpCodeMemReg Op: - Context.EmitLdint(Op.Rn); - Context.EmitLdintzr(Op.Rm); - Context.EmitCast(Op.IntType); - - if (Op.Shift) - { - Context.EmitLsl(Op.Size); - } - - Context.Emit(OpCodes.Add); - break; - } - - //Save address to Scratch var since the register value may change. - Context.Emit(OpCodes.Dup); - - Context.EmitSttmp(); - } - - private static void EmitWBackIfNeeded(AILEmitterCtx Context) - { - //Check whenever the current OpCode has post-indexed write back, if so write it. - //Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both. - if (Context.CurrOp is AOpCodeMemImm Op && Op.WBack) - { - Context.EmitLdtmp(); - - if (Op.PostIdx) - { - Context.EmitLdc_I(Op.Imm); - - Context.Emit(OpCodes.Add); - } - - Context.EmitStint(Op.Rn); - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs deleted file mode 100644 index e59cadd4b8..0000000000 --- a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs +++ /dev/null @@ -1,189 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Memory; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Threading; - -using static ChocolArm64.Instruction.AInstEmitMemoryHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - [Flags] - private enum AccessType - { - None = 0, - Ordered = 1, - Exclusive = 2, - OrderedEx = Ordered | Exclusive - } - - public static void Clrex(AILEmitterCtx Context) - { - EmitMemoryCall(Context, nameof(AMemory.ClearExclusive)); - } - - public static void Dmb(AILEmitterCtx Context) => EmitBarrier(Context); - public static void Dsb(AILEmitterCtx Context) => EmitBarrier(Context); - - public static void Ldar(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Ordered); - public static void Ldaxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.OrderedEx); - public static void Ldxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Exclusive); - public static void Ldxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.Exclusive); - public static void Ldaxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.OrderedEx); - - private static void EmitLdr(AILEmitterCtx Context, AccessType AccType) - { - EmitLoad(Context, AccType, false); - } - - private static void EmitLdp(AILEmitterCtx Context, AccessType AccType) - { - EmitLoad(Context, AccType, true); - } - - private static void EmitLoad(AILEmitterCtx Context, AccessType AccType, bool Pair) - { - AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; - - bool Ordered = (AccType & AccessType.Ordered) != 0; - bool Exclusive = (AccType & AccessType.Exclusive) != 0; - - if (Ordered) - { - EmitBarrier(Context); - } - - if (Exclusive) - { - EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn); - } - - Context.EmitLdint(Op.Rn); - Context.EmitSttmp(); - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdtmp(); - - EmitReadZxCall(Context, Op.Size); - - Context.EmitStintzr(Op.Rt); - - if (Pair) - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdtmp(); - Context.EmitLdc_I8(1 << Op.Size); - - Context.Emit(OpCodes.Add); - - EmitReadZxCall(Context, Op.Size); - - Context.EmitStintzr(Op.Rt2); - } - } - - public static void Pfrm(AILEmitterCtx Context) - { - //Memory Prefetch, execute as no-op. - } - - public static void Stlr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Ordered); - public static void Stlxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.OrderedEx); - public static void Stxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Exclusive); - public static void Stxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.Exclusive); - public static void Stlxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.OrderedEx); - - private static void EmitStr(AILEmitterCtx Context, AccessType AccType) - { - EmitStore(Context, AccType, false); - } - - private static void EmitStp(AILEmitterCtx Context, AccessType AccType) - { - EmitStore(Context, AccType, true); - } - - private static void EmitStore(AILEmitterCtx Context, AccessType AccType, bool Pair) - { - AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; - - bool Ordered = (AccType & AccessType.Ordered) != 0; - bool Exclusive = (AccType & AccessType.Exclusive) != 0; - - if (Ordered) - { - EmitBarrier(Context); - } - - AILLabel LblEx = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - if (Exclusive) - { - EmitMemoryCall(Context, nameof(AMemory.TestExclusive), Op.Rn); - - Context.Emit(OpCodes.Brtrue_S, LblEx); - - Context.EmitLdc_I8(1); - Context.EmitStintzr(Op.Rs); - - Context.Emit(OpCodes.Br_S, LblEnd); - } - - Context.MarkLabel(LblEx); - - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdintzr(Op.Rt); - - EmitWriteCall(Context, Op.Size); - - if (Pair) - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdc_I8(1 << Op.Size); - - Context.Emit(OpCodes.Add); - - Context.EmitLdintzr(Op.Rt2); - - EmitWriteCall(Context, Op.Size); - } - - if (Exclusive) - { - Context.EmitLdc_I8(0); - Context.EmitStintzr(Op.Rs); - - EmitMemoryCall(Context, nameof(AMemory.ClearExclusiveForStore)); - } - - Context.MarkLabel(LblEnd); - } - - private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1) - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - if (Rn != -1) - { - Context.EmitLdint(Rn); - } - - Context.EmitCall(typeof(AMemory), Name); - } - - private static void EmitBarrier(AILEmitterCtx Context) - { - //Note: This barrier is most likely not necessary, and probably - //doesn't make any difference since we need to do a ton of stuff - //(software MMU emulation) to read or write anything anyway. - Context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier)); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs deleted file mode 100644 index b10551fe07..0000000000 --- a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs +++ /dev/null @@ -1,138 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Memory; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static class AInstEmitMemoryHelper - { - private enum Extension - { - Zx, - Sx32, - Sx64 - } - - public static void EmitReadZxCall(AILEmitterCtx Context, int Size) - { - EmitReadCall(Context, Extension.Zx, Size); - } - - public static void EmitReadSx32Call(AILEmitterCtx Context, int Size) - { - EmitReadCall(Context, Extension.Sx32, Size); - } - - public static void EmitReadSx64Call(AILEmitterCtx Context, int Size) - { - EmitReadCall(Context, Extension.Sx64, Size); - } - - private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size) - { - bool IsSimd = GetIsSimd(Context); - - string Name = null; - - if (Size < 0 || Size > (IsSimd ? 4 : 3)) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - if (IsSimd) - { - switch (Size) - { - case 0: Name = nameof(AMemory.ReadVector8); break; - case 1: Name = nameof(AMemory.ReadVector16); break; - case 2: Name = nameof(AMemory.ReadVector32); break; - case 3: Name = nameof(AMemory.ReadVector64); break; - case 4: Name = nameof(AMemory.ReadVector128); break; - } - } - else - { - switch (Size) - { - case 0: Name = nameof(AMemory.ReadByte); break; - case 1: Name = nameof(AMemory.ReadUInt16); break; - case 2: Name = nameof(AMemory.ReadUInt32); break; - case 3: Name = nameof(AMemory.ReadUInt64); break; - } - } - - Context.EmitCall(typeof(AMemory), Name); - - if (!IsSimd) - { - if (Ext == Extension.Sx32 || - Ext == Extension.Sx64) - { - switch (Size) - { - case 0: Context.Emit(OpCodes.Conv_I1); break; - case 1: Context.Emit(OpCodes.Conv_I2); break; - case 2: Context.Emit(OpCodes.Conv_I4); break; - } - } - - if (Size < 3) - { - Context.Emit(Ext == Extension.Sx64 - ? OpCodes.Conv_I8 - : OpCodes.Conv_U8); - } - } - } - - public static void EmitWriteCall(AILEmitterCtx Context, int Size) - { - bool IsSimd = GetIsSimd(Context); - - string Name = null; - - if (Size < 0 || Size > (IsSimd ? 4 : 3)) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - if (Size < 3 && !IsSimd) - { - Context.Emit(OpCodes.Conv_I4); - } - - if (IsSimd) - { - switch (Size) - { - case 0: Name = nameof(AMemory.WriteVector8); break; - case 1: Name = nameof(AMemory.WriteVector16); break; - case 2: Name = nameof(AMemory.WriteVector32); break; - case 3: Name = nameof(AMemory.WriteVector64); break; - case 4: Name = nameof(AMemory.WriteVector128); break; - } - } - else - { - switch (Size) - { - case 0: Name = nameof(AMemory.WriteByte); break; - case 1: Name = nameof(AMemory.WriteUInt16); break; - case 2: Name = nameof(AMemory.WriteUInt32); break; - case 3: Name = nameof(AMemory.WriteUInt64); break; - } - } - - Context.EmitCall(typeof(AMemory), Name); - } - - private static bool GetIsSimd(AILEmitterCtx Context) - { - return Context.CurrOp is IAOpCodeSimd && - !(Context.CurrOp is AOpCodeSimdMemMs || - Context.CurrOp is AOpCodeSimdMemSs); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMove.cs b/ChocolArm64/Instruction/AInstEmitMove.cs deleted file mode 100644 index 719b53d5d5..0000000000 --- a/ChocolArm64/Instruction/AInstEmitMove.cs +++ /dev/null @@ -1,41 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Movk(AILEmitterCtx Context) - { - AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; - - Context.EmitLdintzr(Op.Rd); - Context.EmitLdc_I(~(0xffffL << Op.Pos)); - - Context.Emit(OpCodes.And); - - Context.EmitLdc_I(Op.Imm); - - Context.Emit(OpCodes.Or); - - Context.EmitStintzr(Op.Rd); - } - - public static void Movn(AILEmitterCtx Context) - { - AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; - - Context.EmitLdc_I(~Op.Imm); - Context.EmitStintzr(Op.Rd); - } - - public static void Movz(AILEmitterCtx Context) - { - AOpCodeMov Op = (AOpCodeMov)Context.CurrOp; - - Context.EmitLdc_I(Op.Imm); - Context.EmitStintzr(Op.Rd); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMul.cs b/ChocolArm64/Instruction/AInstEmitMul.cs deleted file mode 100644 index 3713c81f6c..0000000000 --- a/ChocolArm64/Instruction/AInstEmitMul.cs +++ /dev/null @@ -1,80 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Translation; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Madd(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Add); - public static void Msub(AILEmitterCtx Context) => EmitMul(Context, OpCodes.Sub); - - private static void EmitMul(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; - - Context.EmitLdintzr(Op.Ra); - Context.EmitLdintzr(Op.Rn); - Context.EmitLdintzr(Op.Rm); - - Context.Emit(OpCodes.Mul); - Context.Emit(ILOp); - - Context.EmitStintzr(Op.Rd); - } - - public static void Smaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, true); - public static void Smsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, true); - public static void Umaddl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Add, false); - public static void Umsubl(AILEmitterCtx Context) => EmitMull(Context, OpCodes.Sub, false); - - private static void EmitMull(AILEmitterCtx Context, OpCode AddSubOp, bool Signed) - { - AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; - - OpCode CastOp = Signed - ? OpCodes.Conv_I8 - : OpCodes.Conv_U8; - - Context.EmitLdintzr(Op.Ra); - Context.EmitLdintzr(Op.Rn); - - Context.Emit(OpCodes.Conv_I4); - Context.Emit(CastOp); - - Context.EmitLdintzr(Op.Rm); - - Context.Emit(OpCodes.Conv_I4); - Context.Emit(CastOp); - Context.Emit(OpCodes.Mul); - - Context.Emit(AddSubOp); - - Context.EmitStintzr(Op.Rd); - } - - public static void Smulh(AILEmitterCtx Context) - { - AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - Context.EmitLdintzr(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SMulHi128)); - - Context.EmitStintzr(Op.Rd); - } - - public static void Umulh(AILEmitterCtx Context) - { - AOpCodeMul Op = (AOpCodeMul)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - Context.EmitLdintzr(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UMulHi128)); - - Context.EmitStintzr(Op.Rd); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs deleted file mode 100644 index b9aedd07b3..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ /dev/null @@ -1,1500 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Abs_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpSx(Context, () => EmitAbs(Context)); - } - - public static void Abs_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpSx(Context, () => EmitAbs(Context)); - } - - public static void Add_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Add_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Call(Context, nameof(Sse2.Add)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - } - - public static void Addhn_V(AILEmitterCtx Context) - { - EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: false); - } - - public static void Addp_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); - EmitVectorExtractZx(Context, Op.Rn, 1, Op.Size); - - Context.Emit(OpCodes.Add); - - EmitScalarSet(Context, Op.Rd, Op.Size); - } - - public static void Addp_V(AILEmitterCtx Context) - { - EmitVectorPairwiseOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Addv_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); - - for (int Index = 1; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.Emit(OpCodes.Add); - } - - EmitScalarSet(Context, Op.Rd, Op.Size); - } - - public static void Cls_V(AILEmitterCtx Context) - { - MethodInfo MthdInfo = typeof(ASoftFallback).GetMethod(nameof(ASoftFallback.CountLeadingSigns)); - - EmitCountLeadingBits(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Clz_V(AILEmitterCtx Context) - { - MethodInfo MthdInfo = typeof(ASoftFallback).GetMethod(nameof(ASoftFallback.CountLeadingZeros)); - - EmitCountLeadingBits(Context, () => Context.EmitCall(MthdInfo)); - } - - private static void EmitCountLeadingBits(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - int ESize = 8 << Op.Size; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.EmitLdc_I4(ESize); - - Emit(); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Cnt_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 16 : 8; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, 0); - - Context.Emit(OpCodes.Conv_U4); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8)); - - Context.Emit(OpCodes.Conv_U8); - - EmitVectorInsert(Context, Op.Rd, Index, 0); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitAbs(AILEmitterCtx Context) - { - AILLabel LblTrue = new AILLabel(); - - Context.Emit(OpCodes.Dup); - Context.Emit(OpCodes.Ldc_I4_0); - Context.Emit(OpCodes.Bge_S, LblTrue); - - Context.Emit(OpCodes.Neg); - - Context.MarkLabel(LblTrue); - } - - private static void EmitAddLongPairwise(AILEmitterCtx Context, bool Signed, bool Accumulate) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtract(Context, Op.Rn, Idx, Op.Size, Signed); - EmitVectorExtract(Context, Op.Rn, Idx + 1, Op.Size, Signed); - - Context.Emit(OpCodes.Add); - - if (Accumulate) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); - - Context.Emit(OpCodes.Add); - } - - EmitVectorInsertTmp(Context, Index, Op.Size + 1); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitDoublingMultiplyHighHalf(AILEmitterCtx Context, bool Round) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int ESize = 8 << Op.Size; - - Context.Emit(OpCodes.Mul); - - if (!Round) - { - Context.EmitAsr(ESize - 1); - } - else - { - long RoundConst = 1L << (ESize - 1); - - AILLabel LblTrue = new AILLabel(); - - Context.EmitLsl(1); - - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - - Context.EmitAsr(ESize); - - Context.Emit(OpCodes.Dup); - Context.EmitLdc_I8((long)int.MinValue); - Context.Emit(OpCodes.Bne_Un_S, LblTrue); - - Context.Emit(OpCodes.Neg); - - Context.MarkLabel(LblTrue); - } - } - - private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Elems = 8 >> Op.Size; - - int ESize = 8 << Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - long RoundConst = 1L << (ESize - 1); - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); - EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size + 1); - - Emit(); - - if (Round) - { - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - } - - Context.EmitLsr(ESize); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Fabd_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - Context.Emit(OpCodes.Sub); - - EmitUnaryMathCall(Context, nameof(Math.Abs)); - }); - } - - public static void Fabs_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Abs)); - }); - } - - public static void Fabs_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Abs)); - }); - } - - public static void Fadd_S(AILEmitterCtx Context) - { - if (AOptimizations.UseSse && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.AddScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); - } - } - - public static void Fadd_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.Add)); - } - else - { - EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); - } - } - - public static void Faddp_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - EmitVectorExtractF(Context, Op.Rn, 1, SizeF); - - Context.Emit(OpCodes.Add); - - EmitScalarSetF(Context, Op.Rd, SizeF); - } - - public static void Faddp_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - - int Elems = Bytes >> SizeF + 2; - int Half = Elems >> 1; - - for (int Index = 0; Index < Elems; Index++) - { - int Elem = (Index & (Half - 1)) << 1; - - EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, SizeF); - EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, SizeF); - - Context.Emit(OpCodes.Add); - - EmitVectorInsertTmpF(Context, Index, SizeF); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Fdiv_S(AILEmitterCtx Context) - { - if (AOptimizations.UseSse && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.DivideScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); - } - } - - public static void Fdiv_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.Divide)); - } - else - { - EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); - } - } - - public static void Fmadd_S(AILEmitterCtx Context) - { - EmitScalarTernaryRaOpF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Fmax_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max)); - }); - } - - public static void Fmax_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max)); - }); - } - - public static void Fmaxnm_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MaxNum)); - }); - } - - public static void Fmaxnm_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MaxNum)); - }); - } - - public static void Fmin_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min)); - }); - } - - public static void Fmin_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min)); - }); - } - - public static void Fminnm_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MinNum)); - }); - } - - public static void Fminnm_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MinNum)); - }); - } - - public static void Fmla_Se(AILEmitterCtx Context) - { - EmitScalarTernaryOpByElemF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Fmla_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Fmla_Ve(AILEmitterCtx Context) - { - EmitVectorTernaryOpByElemF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Fmls_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Fmls_Ve(AILEmitterCtx Context) - { - EmitVectorTernaryOpByElemF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Fmsub_S(AILEmitterCtx Context) - { - EmitScalarTernaryRaOpF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Fmul_S(AILEmitterCtx Context) - { - if (AOptimizations.UseSse && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.MultiplyScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); - } - } - - public static void Fmul_Se(AILEmitterCtx Context) - { - EmitScalarBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Fmul_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.Multiply)); - } - else - { - EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); - } - } - - public static void Fmul_Ve(AILEmitterCtx Context) - { - EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Fneg_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Fneg_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Fnmadd_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - - Context.Emit(OpCodes.Neg); - - EmitVectorExtractF(Context, Op.Rm, 0, SizeF); - - Context.Emit(OpCodes.Mul); - - EmitVectorExtractF(Context, Op.Ra, 0, SizeF); - - Context.Emit(OpCodes.Sub); - - EmitScalarSetF(Context, Op.Rd, SizeF); - } - - public static void Fnmsub_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - EmitVectorExtractF(Context, Op.Rm, 0, SizeF); - - Context.Emit(OpCodes.Mul); - - EmitVectorExtractF(Context, Op.Ra, 0, SizeF); - - Context.Emit(OpCodes.Sub); - - EmitScalarSetF(Context, Op.Rd, SizeF); - } - - public static void Fnmul_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Neg); - }); - } - - public static void Frecpe_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.RecipEstimate)); - }); - } - - public static void Frecpe_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.RecipEstimate)); - }); - } - - public static void Frecps_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.RecipStep)); - }); - } - - public static void Frecps_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpF(Context, () => - { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.RecipStep)); - }); - } - - public static void Frinta_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Frinta_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); - }); - } - - public static void Frinti_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitScalarUnaryOpF(Context, () => - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr)); - - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.RoundF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Round)); - } - else - { - throw new InvalidOperationException(); - } - }); - } - - public static void Frinti_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - EmitVectorUnaryOpF(Context, () => - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr)); - - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.RoundF)); - } - else if (SizeF == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Round)); - } - else - { - throw new InvalidOperationException(); - } - }); - } - - public static void Frintm_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Floor)); - }); - } - - public static void Frintm_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Floor)); - }); - } - - public static void Frintn_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - EmitRoundMathCall(Context, MidpointRounding.ToEven); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Frintn_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitRoundMathCall(Context, MidpointRounding.ToEven); - }); - } - - public static void Frintp_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Ceiling)); - }); - } - - public static void Frintp_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Ceiling)); - }); - } - - public static void Frintx_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitScalarUnaryOpF(Context, () => - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr)); - - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.RoundF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Round)); - } - else - { - throw new InvalidOperationException(); - } - }); - } - - public static void Frintx_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorUnaryOpF(Context, () => - { - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr)); - - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.RoundF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Round)); - } - else - { - throw new InvalidOperationException(); - } - }); - } - - public static void Frsqrte_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate)); - }); - } - - public static void Frsqrte_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpF(Context, () => - { - EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate)); - }); - } - - public static void Frsqrts_S(AILEmitterCtx Context) - { - EmitFrsqrts(Context, 0, Scalar: true); - } - - public static void Frsqrts_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - - for (int Index = 0; Index < Bytes >> SizeF + 2; Index++) - { - EmitFrsqrts(Context, Index, Scalar: false); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitFrsqrts(AILEmitterCtx Context, int Index, bool Scalar) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (SizeF == 0) - { - Context.EmitLdc_R4(3); - } - else /* if (SizeF == 1) */ - { - Context.EmitLdc_R8(3); - } - - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - EmitVectorExtractF(Context, Op.Rm, Index, SizeF); - - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - - if (SizeF == 0) - { - Context.EmitLdc_R4(0.5f); - } - else /* if (SizeF == 1) */ - { - Context.EmitLdc_R8(0.5); - } - - Context.Emit(OpCodes.Mul); - - if (Scalar) - { - EmitVectorZeroAll(Context, Op.Rd); - } - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); - } - - public static void Fsqrt_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpF(Context, () => - { - EmitUnaryMathCall(Context, nameof(Math.Sqrt)); - }); - } - - public static void Fsub_S(AILEmitterCtx Context) - { - if (AOptimizations.UseSse && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.SubtractScalar)); - } - else - { - EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); - } - } - - public static void Fsub_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.Subtract)); - } - else - { - EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); - } - } - - public static void Mla_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Mla_Ve(AILEmitterCtx Context) - { - EmitVectorTernaryOpByElemZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Mls_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Mls_Ve(AILEmitterCtx Context) - { - EmitVectorTernaryOpByElemZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Mul_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Mul_Ve(AILEmitterCtx Context) - { - EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Neg_S(AILEmitterCtx Context) - { - EmitScalarUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Neg_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Raddhn_V(AILEmitterCtx Context) - { - EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: true); - } - - public static void Rsubhn_V(AILEmitterCtx Context) - { - EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: true); - } - - public static void Saba_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - - Context.Emit(OpCodes.Add); - }); - } - - public static void Sabal_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmTernaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - - Context.Emit(OpCodes.Add); - }); - } - - public static void Sabd_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - }); - } - - public static void Sabdl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - }); - } - - public static void Sadalp_V(AILEmitterCtx Context) - { - EmitAddLongPairwise(Context, Signed: true, Accumulate: true); - } - - public static void Saddl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Saddlp_V(AILEmitterCtx Context) - { - EmitAddLongPairwise(Context, Signed: true, Accumulate: false); - } - - public static void Saddw_V(AILEmitterCtx Context) - { - EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Shadd_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr); - }); - } - - public static void Shsub_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Sub); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr); - }); - } - - public static void Smax_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(long), typeof(long) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); - - EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Smaxp_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(long), typeof(long) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); - - EmitVectorPairwiseOpSx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Smin_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(long), typeof(long) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); - - EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Sminp_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(long), typeof(long) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); - - EmitVectorPairwiseOpSx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Smlal_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmTernaryOpSx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Smlsl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmTernaryOpSx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Smull_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Sqabs_S(AILEmitterCtx Context) - { - EmitScalarSaturatingUnaryOpSx(Context, () => EmitAbs(Context)); - } - - public static void Sqabs_V(AILEmitterCtx Context) - { - EmitVectorSaturatingUnaryOpSx(Context, () => EmitAbs(Context)); - } - - public static void Sqadd_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Add); - } - - public static void Sqadd_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Add); - } - - public static void Sqdmulh_S(AILEmitterCtx Context) - { - EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: false), SaturatingFlags.ScalarSx); - } - - public static void Sqdmulh_V(AILEmitterCtx Context) - { - EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: false), SaturatingFlags.VectorSx); - } - - public static void Sqneg_S(AILEmitterCtx Context) - { - EmitScalarSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Sqneg_V(AILEmitterCtx Context) - { - EmitVectorSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); - } - - public static void Sqrdmulh_S(AILEmitterCtx Context) - { - EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: true), SaturatingFlags.ScalarSx); - } - - public static void Sqrdmulh_V(AILEmitterCtx Context) - { - EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: true), SaturatingFlags.VectorSx); - } - - public static void Sqsub_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Sub); - } - - public static void Sqsub_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Sub); - } - - public static void Sqxtn_S(AILEmitterCtx Context) - { - EmitScalarSaturatingNarrowOpSxSx(Context, () => { }); - } - - public static void Sqxtn_V(AILEmitterCtx Context) - { - EmitVectorSaturatingNarrowOpSxSx(Context, () => { }); - } - - public static void Sqxtun_S(AILEmitterCtx Context) - { - EmitScalarSaturatingNarrowOpSxZx(Context, () => { }); - } - - public static void Sqxtun_V(AILEmitterCtx Context) - { - EmitVectorSaturatingNarrowOpSxZx(Context, () => { }); - } - - public static void Srhadd_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpSx(Context, () => - { - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr); - }); - } - - public static void Ssubl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub)); - } - - public static void Ssubw_V(AILEmitterCtx Context) - { - EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub)); - } - - public static void Sub_S(AILEmitterCtx Context) - { - EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); - } - - public static void Sub_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Call(Context, nameof(Sse2.Subtract)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); - } - } - - public static void Subhn_V(AILEmitterCtx Context) - { - EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: false); - } - - public static void Suqadd_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Accumulate); - } - - public static void Suqadd_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Accumulate); - } - - public static void Uaba_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - - Context.Emit(OpCodes.Add); - }); - } - - public static void Uabal_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - - Context.Emit(OpCodes.Add); - }); - } - - public static void Uabd_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - }); - } - - public static void Uabdl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - EmitAbs(Context); - }); - } - - public static void Uadalp_V(AILEmitterCtx Context) - { - EmitAddLongPairwise(Context, Signed: false, Accumulate: true); - } - - public static void Uaddl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Uaddlp_V(AILEmitterCtx Context) - { - EmitAddLongPairwise(Context, Signed: false, Accumulate: false); - } - - public static void Uaddlv_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); - - for (int Index = 1; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.Emit(OpCodes.Add); - } - - EmitScalarSet(Context, Op.Rd, Op.Size + 1); - } - - public static void Uaddw_V(AILEmitterCtx Context) - { - EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); - } - - public static void Uhadd_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr_Un); - }); - } - - public static void Uhsub_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Sub); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr_Un); - }); - } - - public static void Umax_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); - - EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Umaxp_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); - - EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Umin_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); - - EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Uminp_V(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); - - EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Umlal_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - public static void Umlsl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmTernaryOpZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); - } - - public static void Umull_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); - } - - public static void Uqadd_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Add); - } - - public static void Uqadd_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Add); - } - - public static void Uqsub_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Sub); - } - - public static void Uqsub_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Sub); - } - - public static void Uqxtn_S(AILEmitterCtx Context) - { - EmitScalarSaturatingNarrowOpZxZx(Context, () => { }); - } - - public static void Uqxtn_V(AILEmitterCtx Context) - { - EmitVectorSaturatingNarrowOpZxZx(Context, () => { }); - } - - public static void Urhadd_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Add); - - Context.Emit(OpCodes.Ldc_I4_1); - Context.Emit(OpCodes.Shr_Un); - }); - } - - public static void Usqadd_S(AILEmitterCtx Context) - { - EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate); - } - - public static void Usqadd_V(AILEmitterCtx Context) - { - EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate); - } - - public static void Usubl_V(AILEmitterCtx Context) - { - EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); - } - - public static void Usubw_V(AILEmitterCtx Context) - { - EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs deleted file mode 100644 index 6357396d3c..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs +++ /dev/null @@ -1,525 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitAluHelper; -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Cmeq_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Beq_S, Scalar: true); - } - - public static void Cmeq_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg Op) - { - if (Op.Size < 3 && AOptimizations.UseSse2) - { - EmitSse2Call(Context, nameof(Sse2.CompareEqual)); - } - else if (Op.Size == 3 && AOptimizations.UseSse41) - { - EmitSse41Call(Context, nameof(Sse41.CompareEqual)); - } - else - { - EmitCmp(Context, OpCodes.Beq_S, Scalar: false); - } - } - else - { - EmitCmp(Context, OpCodes.Beq_S, Scalar: false); - } - } - - public static void Cmge_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bge_S, Scalar: true); - } - - public static void Cmge_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bge_S, Scalar: false); - } - - public static void Cmgt_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bgt_S, Scalar: true); - } - - public static void Cmgt_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg Op) - { - if (Op.Size < 3 && AOptimizations.UseSse2) - { - EmitSse2Call(Context, nameof(Sse2.CompareGreaterThan)); - } - else if (Op.Size == 3 && AOptimizations.UseSse42) - { - EmitSse42Call(Context, nameof(Sse42.CompareGreaterThan)); - } - else - { - EmitCmp(Context, OpCodes.Bgt_S, Scalar: false); - } - } - else - { - EmitCmp(Context, OpCodes.Bgt_S, Scalar: false); - } - } - - public static void Cmhi_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bgt_Un_S, Scalar: true); - } - - public static void Cmhi_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bgt_Un_S, Scalar: false); - } - - public static void Cmhs_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bge_Un_S, Scalar: true); - } - - public static void Cmhs_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Bge_Un_S, Scalar: false); - } - - public static void Cmle_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Ble_S, Scalar: true); - } - - public static void Cmle_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Ble_S, Scalar: false); - } - - public static void Cmlt_S(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Blt_S, Scalar: true); - } - - public static void Cmlt_V(AILEmitterCtx Context) - { - EmitCmp(Context, OpCodes.Blt_S, Scalar: false); - } - - public static void Cmtst_S(AILEmitterCtx Context) - { - EmitCmtst(Context, Scalar: true); - } - - public static void Cmtst_V(AILEmitterCtx Context) - { - EmitCmtst(Context, Scalar: false); - } - - public static void Fccmp_S(AILEmitterCtx Context) - { - AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.EmitCondBranch(LblTrue, Op.Cond); - - EmitSetNZCV(Context, Op.NZCV); - - Context.Emit(OpCodes.Br, LblEnd); - - Context.MarkLabel(LblTrue); - - Fcmp_S(Context); - - Context.MarkLabel(LblEnd); - } - - public static void Fccmpe_S(AILEmitterCtx Context) - { - Fccmp_S(Context); - } - - public static void Fcmeq_S(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareEqualScalar)); - } - else - { - EmitScalarFcmp(Context, OpCodes.Beq_S); - } - } - - public static void Fcmeq_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareEqual)); - } - else - { - EmitVectorFcmp(Context, OpCodes.Beq_S); - } - } - - public static void Fcmge_S(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanOrEqualScalar)); - } - else - { - EmitScalarFcmp(Context, OpCodes.Bge_S); - } - } - - public static void Fcmge_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanOrEqual)); - } - else - { - EmitVectorFcmp(Context, OpCodes.Bge_S); - } - } - - public static void Fcmgt_S(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanScalar)); - } - else - { - EmitScalarFcmp(Context, OpCodes.Bgt_S); - } - } - - public static void Fcmgt_V(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse - && AOptimizations.UseSse2) - { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThan)); - } - else - { - EmitVectorFcmp(Context, OpCodes.Bgt_S); - } - } - - public static void Fcmle_S(AILEmitterCtx Context) - { - EmitScalarFcmp(Context, OpCodes.Ble_S); - } - - public static void Fcmle_V(AILEmitterCtx Context) - { - EmitVectorFcmp(Context, OpCodes.Ble_S); - } - - public static void Fcmlt_S(AILEmitterCtx Context) - { - EmitScalarFcmp(Context, OpCodes.Blt_S); - } - - public static void Fcmlt_V(AILEmitterCtx Context) - { - EmitVectorFcmp(Context, OpCodes.Blt_S); - } - - public static void Fcmp_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false; - - //Handle NaN case. - //If any number is NaN, then NZCV = 0011. - if (CmpWithZero) - { - EmitNaNCheck(Context, Op.Rn); - } - else - { - EmitNaNCheck(Context, Op.Rn); - EmitNaNCheck(Context, Op.Rm); - - Context.Emit(OpCodes.Or); - } - - AILLabel LblNaN = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.Emit(OpCodes.Brtrue_S, LblNaN); - - void EmitLoadOpers() - { - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - if (CmpWithZero) - { - if (Op.Size == 0) - { - Context.EmitLdc_R4(0); - } - else /* if (SizeF == 1) */ - { - Context.EmitLdc_R8(0); - } - } - else - { - EmitVectorExtractF(Context, Op.Rm, 0, Op.Size); - } - } - - //Z = Rn == Rm - EmitLoadOpers(); - - Context.Emit(OpCodes.Ceq); - Context.Emit(OpCodes.Dup); - - Context.EmitStflg((int)APState.ZBit); - - //C = Rn >= Rm - EmitLoadOpers(); - - Context.Emit(OpCodes.Cgt); - Context.Emit(OpCodes.Or); - - Context.EmitStflg((int)APState.CBit); - - //N = Rn < Rm - EmitLoadOpers(); - - Context.Emit(OpCodes.Clt); - - Context.EmitStflg((int)APState.NBit); - - //V = 0 - Context.EmitLdc_I4(0); - - Context.EmitStflg((int)APState.VBit); - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblNaN); - - EmitSetNZCV(Context, 0b0011); - - Context.MarkLabel(LblEnd); - } - - public static void Fcmpe_S(AILEmitterCtx Context) - { - Fcmp_S(Context); - } - - private static void EmitNaNCheck(AILEmitterCtx Context, int Reg) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - EmitVectorExtractF(Context, Reg, 0, Op.Size); - - if (Op.Size == 0) - { - Context.EmitCall(typeof(float), nameof(float.IsNaN)); - } - else if (Op.Size == 1) - { - Context.EmitCall(typeof(double), nameof(double.IsNaN)); - } - else - { - throw new InvalidOperationException(); - } - } - - private static void EmitCmp(AILEmitterCtx Context, OpCode ILOp, bool Scalar) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); - - if (Op is AOpCodeSimdReg BinOp) - { - EmitVectorExtractSx(Context, BinOp.Rm, Index, Op.Size); - } - else - { - Context.EmitLdc_I8(0); - } - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.Emit(ILOp, LblTrue); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0); - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask); - - Context.MarkLabel(LblEnd); - } - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitCmtst(AILEmitterCtx Context, bool Scalar) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size); - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.Emit(OpCodes.And); - - Context.EmitLdc_I8(0); - - Context.Emit(OpCodes.Bne_Un_S, LblTrue); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0); - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask); - - Context.MarkLabel(LblEnd); - } - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitScalarFcmp(AILEmitterCtx Context, OpCode ILOp) - { - EmitFcmp(Context, ILOp, 0, Scalar: true); - } - - private static void EmitVectorFcmp(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - - for (int Index = 0; Index < Bytes >> SizeF + 2; Index++) - { - EmitFcmp(Context, ILOp, Index, Scalar: false); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitFcmp(AILEmitterCtx Context, OpCode ILOp, int Index, bool Scalar) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - ulong SzMask = ulong.MaxValue >> (64 - (32 << SizeF)); - - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - - if (Op is AOpCodeSimdReg BinOp) - { - EmitVectorExtractF(Context, BinOp.Rm, Index, SizeF); - } - else if (SizeF == 0) - { - Context.EmitLdc_R4(0); - } - else /* if (SizeF == 1) */ - { - Context.EmitLdc_R8(0); - } - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.Emit(ILOp, LblTrue); - - if (Scalar) - { - EmitVectorZeroAll(Context, Op.Rd); - } - else - { - EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, 0); - } - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - if (Scalar) - { - EmitVectorInsert(Context, Op.Rd, Index, 3, (long)SzMask); - - EmitVectorZeroUpper(Context, Op.Rd); - } - else - { - EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, (long)SzMask); - } - - Context.MarkLabel(LblEnd); - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSimdCrypto.cs b/ChocolArm64/Instruction/AInstEmitSimdCrypto.cs deleted file mode 100644 index b2680a588a..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdCrypto.cs +++ /dev/null @@ -1,54 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Translation; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Aesd_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Decrypt)); - - Context.EmitStvec(Op.Rd); - } - - public static void Aese_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Encrypt)); - - Context.EmitStvec(Op.Rd); - } - - public static void Aesimc_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InverseMixColumns)); - - Context.EmitStvec(Op.Rd); - } - - public static void Aesmc_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MixColumns)); - - Context.EmitStvec(Op.Rd); - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs deleted file mode 100644 index 231de0aff7..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs +++ /dev/null @@ -1,640 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Fcvt_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - EmitFloatCast(Context, Op.Opc); - - EmitScalarSetF(Context, Op.Rd, Op.Opc); - } - - public static void Fcvtas_Gp(AILEmitterCtx Context) - { - EmitFcvt_s_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero)); - } - - public static void Fcvtau_Gp(AILEmitterCtx Context) - { - EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero)); - } - - public static void Fcvtl_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Elems = 4 >> SizeF; - - int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - if (SizeF == 0) - { - EmitVectorExtractZx(Context, Op.Rn, Part + Index, 1); - Context.Emit(OpCodes.Conv_U2); - - Context.EmitCall(typeof(ASoftFloat), nameof(ASoftFloat.ConvertHalfToSingle)); - } - else /* if (SizeF == 1) */ - { - EmitVectorExtractF(Context, Op.Rn, Part + Index, 0); - - Context.Emit(OpCodes.Conv_R8); - } - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); - } - } - - public static void Fcvtms_Gp(AILEmitterCtx Context) - { - EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); - } - - public static void Fcvtmu_Gp(AILEmitterCtx Context) - { - EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); - } - - public static void Fcvtn_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Elems = 4 >> SizeF; - - int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractF(Context, Op.Rd, Index, SizeF); - - if (SizeF == 0) - { - //TODO: This need the half precision floating point type, - //that is not yet supported on .NET. We should probably - //do our own implementation on the meantime. - throw new NotImplementedException(); - } - else /* if (SizeF == 1) */ - { - Context.Emit(OpCodes.Conv_R4); - - EmitVectorInsertF(Context, Op.Rd, Part + Index, 0); - } - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Fcvtns_S(AILEmitterCtx Context) - { - EmitFcvtn(Context, Signed: true, Scalar: true); - } - - public static void Fcvtns_V(AILEmitterCtx Context) - { - EmitFcvtn(Context, Signed: true, Scalar: false); - } - - public static void Fcvtnu_S(AILEmitterCtx Context) - { - EmitFcvtn(Context, Signed: false, Scalar: true); - } - - public static void Fcvtnu_V(AILEmitterCtx Context) - { - EmitFcvtn(Context, Signed: false, Scalar: false); - } - - public static void Fcvtps_Gp(AILEmitterCtx Context) - { - EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); - } - - public static void Fcvtpu_Gp(AILEmitterCtx Context) - { - EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); - } - - public static void Fcvtzs_Gp(AILEmitterCtx Context) - { - EmitFcvt_s_Gp(Context, () => { }); - } - - public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context) - { - EmitFcvtzs_Gp_Fix(Context); - } - - public static void Fcvtzs_S(AILEmitterCtx Context) - { - EmitScalarFcvtzs(Context); - } - - public static void Fcvtzs_V(AILEmitterCtx Context) - { - EmitVectorFcvtzs(Context); - } - - public static void Fcvtzu_Gp(AILEmitterCtx Context) - { - EmitFcvt_u_Gp(Context, () => { }); - } - - public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context) - { - EmitFcvtzu_Gp_Fix(Context); - } - - public static void Fcvtzu_S(AILEmitterCtx Context) - { - EmitScalarFcvtzu(Context); - } - - public static void Fcvtzu_V(AILEmitterCtx Context) - { - EmitVectorFcvtzu(Context); - } - - public static void Scvtf_Gp(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U4); - } - - EmitFloatCast(Context, Op.Size); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Scvtf_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size + 2); - - EmitFloatCast(Context, Op.Size); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Scvtf_V(AILEmitterCtx Context) - { - EmitVectorCvtf(Context, Signed: true); - } - - public static void Ucvtf_Gp(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U4); - } - - Context.Emit(OpCodes.Conv_R_Un); - - EmitFloatCast(Context, Op.Size); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Ucvtf_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size + 2); - - Context.Emit(OpCodes.Conv_R_Un); - - EmitFloatCast(Context, Op.Size); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Ucvtf_V(AILEmitterCtx Context) - { - EmitVectorCvtf(Context, Signed: false); - } - - private static int GetFBits(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdShImm Op) - { - return GetImmShr(Op); - } - - return 0; - } - - private static void EmitFloatCast(AILEmitterCtx Context, int Size) - { - if (Size == 0) - { - Context.Emit(OpCodes.Conv_R4); - } - else if (Size == 1) - { - Context.Emit(OpCodes.Conv_R8); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - } - - private static void EmitFcvtn(AILEmitterCtx Context, bool Signed, bool Scalar) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> SizeI : 1; - - if (Scalar && (SizeF == 0)) - { - EmitVectorZeroLowerTmp(Context); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - - EmitRoundMathCall(Context, MidpointRounding.ToEven); - - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF32ToS32) - : nameof(AVectorHelper.SatF32ToU32)); - - Context.Emit(OpCodes.Conv_U8); - } - else /* if (SizeF == 1) */ - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF64ToS64) - : nameof(AVectorHelper.SatF64ToU64)); - } - - EmitVectorInsertTmp(Context, Index, SizeI); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitFcvt_s_Gp(AILEmitterCtx Context, Action Emit) - { - EmitFcvt___Gp(Context, Emit, true); - } - - private static void EmitFcvt_u_Gp(AILEmitterCtx Context, Action Emit) - { - EmitFcvt___Gp(Context, Emit, false); - } - - private static void EmitFcvt___Gp(AILEmitterCtx Context, Action Emit, bool Signed) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - Emit(); - - if (Signed) - { - EmitScalarFcvts(Context, Op.Size, 0); - } - else - { - EmitScalarFcvtu(Context, Op.Size, 0); - } - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitFcvtzs_Gp_Fix(AILEmitterCtx Context) - { - EmitFcvtz__Gp_Fix(Context, true); - } - - private static void EmitFcvtzu_Gp_Fix(AILEmitterCtx Context) - { - EmitFcvtz__Gp_Fix(Context, false); - } - - private static void EmitFcvtz__Gp_Fix(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - if (Signed) - { - EmitScalarFcvts(Context, Op.Size, Op.FBits); - } - else - { - EmitScalarFcvtu(Context, Op.Size, Op.FBits); - } - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitVectorScvtf(AILEmitterCtx Context) - { - EmitVectorCvtf(Context, true); - } - - private static void EmitVectorUcvtf(AILEmitterCtx Context) - { - EmitVectorCvtf(Context, false); - } - - private static void EmitVectorCvtf(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int FBits = GetFBits(Context); - - int Bytes = Op.GetBitsCount() >> 3; - - for (int Index = 0; Index < (Bytes >> SizeI); Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, SizeI, Signed); - - if (!Signed) - { - Context.Emit(OpCodes.Conv_R_Un); - } - - Context.Emit(SizeF == 0 - ? OpCodes.Conv_R4 - : OpCodes.Conv_R8); - - EmitI2fFBitsMul(Context, SizeF, FBits); - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitScalarFcvtzs(AILEmitterCtx Context) - { - EmitScalarFcvtz(Context, true); - } - - private static void EmitScalarFcvtzu(AILEmitterCtx Context) - { - EmitScalarFcvtz(Context, false); - } - - private static void EmitScalarFcvtz(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int FBits = GetFBits(Context); - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - - EmitF2iFBitsMul(Context, SizeF, FBits); - - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF32ToS32) - : nameof(AVectorHelper.SatF32ToU32)); - } - else /* if (SizeF == 1) */ - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF64ToS64) - : nameof(AVectorHelper.SatF64ToU64)); - } - - if (SizeF == 0) - { - Context.Emit(OpCodes.Conv_U8); - } - - EmitScalarSet(Context, Op.Rd, SizeI); - } - - private static void EmitVectorFcvtzs(AILEmitterCtx Context) - { - EmitVectorFcvtz(Context, true); - } - - private static void EmitVectorFcvtzu(AILEmitterCtx Context) - { - EmitVectorFcvtz(Context, false); - } - - private static void EmitVectorFcvtz(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int FBits = GetFBits(Context); - - int Bytes = Op.GetBitsCount() >> 3; - - for (int Index = 0; Index < (Bytes >> SizeI); Index++) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - - EmitF2iFBitsMul(Context, SizeF, FBits); - - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF32ToS32) - : nameof(AVectorHelper.SatF32ToU32)); - } - else /* if (SizeF == 1) */ - { - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.SatF64ToS64) - : nameof(AVectorHelper.SatF64ToU64)); - } - - if (SizeF == 0) - { - Context.Emit(OpCodes.Conv_U8); - } - - EmitVectorInsert(Context, Op.Rd, Index, SizeI); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitScalarFcvts(AILEmitterCtx Context, int Size, int FBits) - { - if (Size < 0 || Size > 1) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - EmitF2iFBitsMul(Context, Size, FBits); - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF32ToS32)); - } - else /* if (Size == 1) */ - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF64ToS32)); - } - } - else - { - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF32ToS64)); - } - else /* if (Size == 1) */ - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF64ToS64)); - } - } - } - - private static void EmitScalarFcvtu(AILEmitterCtx Context, int Size, int FBits) - { - if (Size < 0 || Size > 1) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - EmitF2iFBitsMul(Context, Size, FBits); - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF32ToU32)); - } - else /* if (Size == 1) */ - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF64ToU32)); - } - } - else - { - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF32ToU64)); - } - else /* if (Size == 1) */ - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.SatF64ToU64)); - } - } - } - - private static void EmitF2iFBitsMul(AILEmitterCtx Context, int Size, int FBits) - { - if (FBits != 0) - { - if (Size == 0) - { - Context.EmitLdc_R4(MathF.Pow(2, FBits)); - } - else if (Size == 1) - { - Context.EmitLdc_R8(Math.Pow(2, FBits)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.Emit(OpCodes.Mul); - } - } - - private static void EmitI2fFBitsMul(AILEmitterCtx Context, int Size, int FBits) - { - if (FBits != 0) - { - if (Size == 0) - { - Context.EmitLdc_R4(1f / MathF.Pow(2, FBits)); - } - else if (Size == 1) - { - Context.EmitLdc_R8(1 / Math.Pow(2, FBits)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.Emit(OpCodes.Mul); - } - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSimdHash.cs b/ChocolArm64/Instruction/AInstEmitSimdHash.cs deleted file mode 100644 index 6b642acb58..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdHash.cs +++ /dev/null @@ -1,61 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Translation; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { -#region "Sha256" - public static void Sha256h_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.HashLower)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha256h2_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.HashUpper)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha256su0_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SchedulePart1)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sha256su1_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SchedulePart2)); - - Context.EmitStvec(Op.Rd); - } -#endregion - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs deleted file mode 100644 index cb884c1ac8..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ /dev/null @@ -1,1400 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace ChocolArm64.Instruction -{ - static class AInstEmitSimdHelper - { - [Flags] - public enum OperFlags - { - Rd = 1 << 0, - Rn = 1 << 1, - Rm = 1 << 2, - Ra = 1 << 3, - - RnRm = Rn | Rm, - RdRn = Rd | Rn, - RaRnRm = Ra | Rn | Rm, - RdRnRm = Rd | Rn | Rm - } - - public static int GetImmShl(AOpCodeSimdShImm Op) - { - return Op.Imm - (8 << Op.Size); - } - - public static int GetImmShr(AOpCodeSimdShImm Op) - { - return (8 << (Op.Size + 1)) - Op.Imm; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EmitSse2Call(AILEmitterCtx Context, string Name) - { - EmitSseCall(Context, Name, typeof(Sse2)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EmitSse41Call(AILEmitterCtx Context, string Name) - { - EmitSseCall(Context, Name, typeof(Sse41)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EmitSse42Call(AILEmitterCtx Context, string Name) - { - EmitSseCall(Context, Name, typeof(Sse42)); - } - - private static void EmitSseCall(AILEmitterCtx Context, string Name, Type Type) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - void Ldvec(int Reg) - { - Context.EmitLdvec(Reg); - - switch (Op.Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToSByte)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt16)); break; - case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt32)); break; - case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt64)); break; - } - } - - Ldvec(Op.Rn); - - Type BaseType = null; - - switch (Op.Size) - { - case 0: BaseType = typeof(Vector128); break; - case 1: BaseType = typeof(Vector128); break; - case 2: BaseType = typeof(Vector128); break; - case 3: BaseType = typeof(Vector128); break; - } - - if (Op is AOpCodeSimdReg BinOp) - { - Ldvec(BinOp.Rm); - - Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType, BaseType })); - } - else - { - Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType })); - } - - switch (Op.Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSByteToSingle)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16ToSingle)); break; - case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32ToSingle)); break; - case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt64ToSingle)); break; - } - - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitScalarSseOrSse2CallF(AILEmitterCtx Context, string Name) - { - EmitSseOrSse2CallF(Context, Name, true); - } - - public static void EmitVectorSseOrSse2CallF(AILEmitterCtx Context, string Name) - { - EmitSseOrSse2CallF(Context, Name, false); - } - - public static void EmitSseOrSse2CallF(AILEmitterCtx Context, string Name, bool Scalar) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - void Ldvec(int Reg) - { - Context.EmitLdvec(Reg); - - if (SizeF == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToDouble)); - } - } - - Ldvec(Op.Rn); - - Type Type; - Type BaseType; - - if (SizeF == 0) - { - Type = typeof(Sse); - BaseType = typeof(Vector128); - } - else /* if (SizeF == 1) */ - { - Type = typeof(Sse2); - BaseType = typeof(Vector128); - } - - if (Op is AOpCodeSimdReg BinOp) - { - Ldvec(BinOp.Rm); - - Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType, BaseType })); - } - else - { - Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType })); - } - - if (SizeF == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleToSingle)); - } - - Context.EmitStvec(Op.Rd); - - if (Scalar) - { - if (SizeF == 0) - { - EmitVectorZero32_128(Context, Op.Rd); - } - else /* if (SizeF == 1) */ - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - else if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitUnaryMathCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - MethodInfo MthdInfo; - - if (SizeF == 0) - { - MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double) }); - } - - Context.EmitCall(MthdInfo); - } - - public static void EmitBinaryMathCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - MethodInfo MthdInfo; - - if (SizeF == 0) - { - MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); - } - - Context.EmitCall(MthdInfo); - } - - public static void EmitRoundMathCall(AILEmitterCtx Context, MidpointRounding RoundMode) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - Context.EmitLdc_I4((int)RoundMode); - - MethodInfo MthdInfo; - - Type[] Types = new Type[] { null, typeof(MidpointRounding) }; - - Types[0] = SizeF == 0 - ? typeof(float) - : typeof(double); - - if (SizeF == 0) - { - MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types); - } - - Context.EmitCall(MthdInfo); - } - - public static void EmitUnarySoftFloatCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - MethodInfo MthdInfo; - - if (SizeF == 0) - { - MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double) }); - } - - Context.EmitCall(MthdInfo); - } - - public static void EmitBinarySoftFloatCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - MethodInfo MthdInfo; - - if (SizeF == 0) - { - MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); - } - - Context.EmitCall(MthdInfo); - } - - public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; - - EmitScalarOpByElemF(Context, Emit, Op.Index, Ternary: false); - } - - public static void EmitScalarTernaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; - - EmitScalarOpByElemF(Context, Emit, Op.Index, Ternary: true); - } - - public static void EmitScalarOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (Ternary) - { - EmitVectorExtractF(Context, Op.Rd, 0, SizeF); - } - - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - EmitVectorExtractF(Context, Op.Rm, Elem, SizeF); - - Emit(); - - EmitScalarSetF(Context, Op.Rd, SizeF); - } - - public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitScalarOp(Context, Emit, OperFlags.Rn, true); - } - - public static void EmitScalarBinaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitScalarOp(Context, Emit, OperFlags.RnRm, true); - } - - public static void EmitScalarUnaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitScalarOp(Context, Emit, OperFlags.Rn, false); - } - - public static void EmitScalarBinaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitScalarOp(Context, Emit, OperFlags.RnRm, false); - } - - public static void EmitScalarTernaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitScalarOp(Context, Emit, OperFlags.RdRnRm, false); - } - - public static void EmitScalarOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - bool Rd = (Opers & OperFlags.Rd) != 0; - bool Rn = (Opers & OperFlags.Rn) != 0; - bool Rm = (Opers & OperFlags.Rm) != 0; - - if (Rd) - { - EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed); - } - - if (Rn) - { - EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed); - } - - if (Rm) - { - EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, 0, Op.Size, Signed); - } - - Emit(); - - EmitScalarSet(Context, Op.Rd, Op.Size); - } - - public static void EmitScalarUnaryOpF(AILEmitterCtx Context, Action Emit) - { - EmitScalarOpF(Context, Emit, OperFlags.Rn); - } - - public static void EmitScalarBinaryOpF(AILEmitterCtx Context, Action Emit) - { - EmitScalarOpF(Context, Emit, OperFlags.RnRm); - } - - public static void EmitScalarTernaryRaOpF(AILEmitterCtx Context, Action Emit) - { - EmitScalarOpF(Context, Emit, OperFlags.RaRnRm); - } - - public static void EmitScalarOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - bool Ra = (Opers & OperFlags.Ra) != 0; - bool Rn = (Opers & OperFlags.Rn) != 0; - bool Rm = (Opers & OperFlags.Rm) != 0; - - if (Ra) - { - EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF); - } - - if (Rn) - { - EmitVectorExtractF(Context, Op.Rn, 0, SizeF); - } - - if (Rm) - { - EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF); - } - - Emit(); - - EmitScalarSetF(Context, Op.Rd, SizeF); - } - - public static void EmitVectorUnaryOpF(AILEmitterCtx Context, Action Emit) - { - EmitVectorOpF(Context, Emit, OperFlags.Rn); - } - - public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit) - { - EmitVectorOpF(Context, Emit, OperFlags.RnRm); - } - - public static void EmitVectorTernaryOpF(AILEmitterCtx Context, Action Emit) - { - EmitVectorOpF(Context, Emit, OperFlags.RdRnRm); - } - - public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> SizeF + 2; - - bool Rd = (Opers & OperFlags.Rd) != 0; - bool Rn = (Opers & OperFlags.Rn) != 0; - bool Rm = (Opers & OperFlags.Rm) != 0; - - for (int Index = 0; Index < Elems; Index++) - { - if (Rd) - { - EmitVectorExtractF(Context, Op.Rd, Index, SizeF); - } - - if (Rn) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - } - - if (Rm) - { - EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, Index, SizeF); - } - - Emit(); - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; - - EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false); - } - - public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; - - EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true); - } - - public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> SizeF + 2; - - for (int Index = 0; Index < Elems; Index++) - { - if (Ternary) - { - EmitVectorExtractF(Context, Op.Rd, Index, SizeF); - } - - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - EmitVectorExtractF(Context, Op.Rm, Elem, SizeF); - - Emit(); - - EmitVectorInsertTmpF(Context, Index, SizeF); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitVectorUnaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.Rn, true); - } - - public static void EmitVectorBinaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.RnRm, true); - } - - public static void EmitVectorTernaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.RdRnRm, true); - } - - public static void EmitVectorUnaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.Rn, false); - } - - public static void EmitVectorBinaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.RnRm, false); - } - - public static void EmitVectorTernaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.RdRnRm, false); - } - - public static void EmitVectorOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - bool Rd = (Opers & OperFlags.Rd) != 0; - bool Rn = (Opers & OperFlags.Rn) != 0; - bool Rm = (Opers & OperFlags.Rm) != 0; - - for (int Index = 0; Index < Elems; Index++) - { - if (Rd) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - } - - if (Rn) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - } - - if (Rm) - { - EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); - } - - Emit(); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitVectorBinaryOpByElemSx(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElem(Context, Emit, Op.Index, false, true); - } - - public static void EmitVectorBinaryOpByElemZx(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElem(Context, Emit, Op.Index, false, false); - } - - public static void EmitVectorTernaryOpByElemZx(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElem(Context, Emit, Op.Index, true, false); - } - - public static void EmitVectorOpByElem(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary, bool Signed) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - EmitVectorExtract(Context, Op.Rm, Elem, Op.Size, Signed); - Context.EmitSttmp(); - - for (int Index = 0; Index < Elems; Index++) - { - if (Ternary) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - } - - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - Context.EmitLdtmp(); - - Emit(); - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit) - { - EmitVectorImmOp(Context, Emit, false); - } - - public static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit) - { - EmitVectorImmOp(Context, Emit, true); - } - - public static void EmitVectorImmOp(AILEmitterCtx Context, Action Emit, bool Binary) - { - AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; Index++) - { - if (Binary) - { - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - } - - Context.EmitLdc_I8(Op.Imm); - - Emit(); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitVectorWidenRmBinaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRmBinaryOp(Context, Emit, true); - } - - public static void EmitVectorWidenRmBinaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRmBinaryOp(Context, Emit, false); - } - - public static void EmitVectorWidenRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Elems = 8 >> Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); - EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); - - Emit(); - - EmitVectorInsertTmp(Context, Index, Op.Size + 1); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - } - - public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRnRmOp(Context, Emit, false, true); - } - - public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRnRmOp(Context, Emit, false, false); - } - - public static void EmitVectorWidenRnRmTernaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRnRmOp(Context, Emit, true, true); - } - - public static void EmitVectorWidenRnRmTernaryOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorWidenRnRmOp(Context, Emit, true, false); - } - - public static void EmitVectorWidenRnRmOp(AILEmitterCtx Context, Action Emit, bool Ternary, bool Signed) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Elems = 8 >> Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - if (Ternary) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); - } - - EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); - EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); - - Emit(); - - EmitVectorInsertTmp(Context, Index, Op.Size + 1); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - } - - public static void EmitVectorPairwiseOpSx(AILEmitterCtx Context, Action Emit) - { - EmitVectorPairwiseOp(Context, Emit, true); - } - - public static void EmitVectorPairwiseOpZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorPairwiseOp(Context, Emit, false); - } - - public static void EmitVectorPairwiseOp(AILEmitterCtx Context, Action Emit, bool Signed) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtract(Context, Op.Rn, Idx, Op.Size, Signed); - EmitVectorExtract(Context, Op.Rn, Idx + 1, Op.Size, Signed); - - Emit(); - - EmitVectorExtract(Context, Op.Rm, Idx, Op.Size, Signed); - EmitVectorExtract(Context, Op.Rm, Idx + 1, Op.Size, Signed); - - Emit(); - - EmitVectorInsertTmp(Context, Pairs + Index, Op.Size); - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - [Flags] - public enum SaturatingFlags - { - Scalar = 1 << 0, - Signed = 1 << 1, - - Add = 1 << 2, - Sub = 1 << 3, - - Accumulate = 1 << 4, - - ScalarSx = Scalar | Signed, - ScalarZx = Scalar, - - VectorSx = Signed, - VectorZx = 0 - } - - public static void EmitScalarSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingUnaryOpSx(Context, Emit, SaturatingFlags.ScalarSx); - } - - public static void EmitVectorSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingUnaryOpSx(Context, Emit, SaturatingFlags.VectorSx); - } - - public static void EmitSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit, SaturatingFlags Flags) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - bool Scalar = (Flags & SaturatingFlags.Scalar) != 0; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); - - Emit(); - - if (Op.Size <= 2) - { - EmitSatQ(Context, Op.Size, true, true); - } - else /* if (Op.Size == 3) */ - { - EmitUnarySignedSatQAbsOrNeg(Context); - } - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void EmitScalarSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags) - { - EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.ScalarSx | Flags); - } - - public static void EmitScalarSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags) - { - EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.ScalarZx | Flags); - } - - public static void EmitVectorSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags) - { - EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.VectorSx | Flags); - } - - public static void EmitVectorSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags) - { - EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.VectorZx | Flags); - } - - public static void EmitSaturatingBinaryOp(AILEmitterCtx Context, Action Emit, SaturatingFlags Flags) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - bool Scalar = (Flags & SaturatingFlags.Scalar) != 0; - bool Signed = (Flags & SaturatingFlags.Signed) != 0; - - bool Add = (Flags & SaturatingFlags.Add) != 0; - bool Sub = (Flags & SaturatingFlags.Sub) != 0; - - bool Accumulate = (Flags & SaturatingFlags.Accumulate) != 0; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } - - if (Add || Sub) - { - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); - - if (Op.Size <= 2) - { - Context.Emit(Add ? OpCodes.Add : OpCodes.Sub); - - EmitSatQ(Context, Op.Size, true, Signed); - } - else /* if (Op.Size == 3) */ - { - if (Add) - { - EmitBinarySatQAdd(Context, Signed); - } - else /* if (Sub) */ - { - EmitBinarySatQSub(Context, Signed); - } - } - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - } - else if (Accumulate) - { - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, !Signed); - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - - if (Op.Size <= 2) - { - Context.Emit(OpCodes.Add); - - EmitSatQ(Context, Op.Size, true, Signed); - } - else /* if (Op.Size == 3) */ - { - EmitBinarySatQAccumulate(Context, Signed); - } - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - } - else - { - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); - - Emit(); - - EmitSatQ(Context, Op.Size, true, Signed); - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - [Flags] - public enum SaturatingNarrowFlags - { - Scalar = 1 << 0, - SignedSrc = 1 << 1, - SignedDst = 1 << 2, - - ScalarSxSx = Scalar | SignedSrc | SignedDst, - ScalarSxZx = Scalar | SignedSrc, - ScalarZxSx = Scalar | SignedDst, - ScalarZxZx = Scalar, - - VectorSxSx = SignedSrc | SignedDst, - VectorSxZx = SignedSrc, - VectorZxSx = SignedDst, - VectorZxZx = 0 - } - - public static void EmitScalarSaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarSxSx); - } - - public static void EmitScalarSaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarSxZx); - } - - public static void EmitScalarSaturatingNarrowOpZxSx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarZxSx); - } - - public static void EmitScalarSaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarZxZx); - } - - public static void EmitVectorSaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorSxSx); - } - - public static void EmitVectorSaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorSxZx); - } - - public static void EmitVectorSaturatingNarrowOpZxSx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorZxSx); - } - - public static void EmitVectorSaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit) - { - EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorZxZx); - } - - public static void EmitSaturatingNarrowOp(AILEmitterCtx Context, Action Emit, SaturatingNarrowFlags Flags) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - bool Scalar = (Flags & SaturatingNarrowFlags.Scalar) != 0; - bool SignedSrc = (Flags & SaturatingNarrowFlags.SignedSrc) != 0; - bool SignedDst = (Flags & SaturatingNarrowFlags.SignedDst) != 0; - - int Elems = !Scalar ? 8 >> Op.Size : 1; - - int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; - - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, SignedSrc); - - Emit(); - - EmitSatQ(Context, Op.Size, SignedSrc, SignedDst); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - // TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned). - public static void EmitSatQ( - AILEmitterCtx Context, - int SizeDst, - bool SignedSrc, - bool SignedDst) - { - if (SizeDst > 2) - { - throw new ArgumentOutOfRangeException(nameof(SizeDst)); - } - - Context.EmitLdc_I4(SizeDst); - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - if (SignedSrc) - { - ASoftFallback.EmitCall(Context, SignedDst - ? nameof(ASoftFallback.SignedSrcSignedDstSatQ) - : nameof(ASoftFallback.SignedSrcUnsignedDstSatQ)); - } - else - { - ASoftFallback.EmitCall(Context, SignedDst - ? nameof(ASoftFallback.UnsignedSrcSignedDstSatQ) - : nameof(ASoftFallback.UnsignedSrcUnsignedDstSatQ)); - } - } - - // TSrc (64bit) == TDst (64bit); signed. - public static void EmitUnarySignedSatQAbsOrNeg(AILEmitterCtx Context) - { - if (((AOpCodeSimd)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UnarySignedSatQAbsOrNeg)); - } - - // TSrcs (64bit) == TDst (64bit); signed, unsigned. - public static void EmitBinarySatQAdd(AILEmitterCtx Context, bool Signed) - { - if (((AOpCodeSimdReg)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.BinarySignedSatQAdd) - : nameof(ASoftFallback.BinaryUnsignedSatQAdd)); - } - - // TSrcs (64bit) == TDst (64bit); signed, unsigned. - public static void EmitBinarySatQSub(AILEmitterCtx Context, bool Signed) - { - if (((AOpCodeSimdReg)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.BinarySignedSatQSub) - : nameof(ASoftFallback.BinaryUnsignedSatQSub)); - } - - // TSrcs (64bit) == TDst (64bit); signed, unsigned. - public static void EmitBinarySatQAccumulate(AILEmitterCtx Context, bool Signed) - { - if (((AOpCodeSimd)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.BinarySignedSatQAcc) - : nameof(ASoftFallback.BinaryUnsignedSatQAcc)); - } - - public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size) - { - EmitVectorZeroAll(Context, Reg); - EmitVectorInsert(Context, Reg, 0, Size); - } - - public static void EmitScalarSetF(AILEmitterCtx Context, int Reg, int Size) - { - EmitVectorZeroAll(Context, Reg); - EmitVectorInsertF(Context, Reg, 0, Size); - } - - public static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size) - { - EmitVectorExtract(Context, Reg, Index, Size, true); - } - - public static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index, int Size) - { - EmitVectorExtract(Context, Reg, Index, Size, false); - } - - public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed) - { - ThrowIfInvalid(Index, Size); - - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, Signed - ? nameof(AVectorHelper.VectorExtractIntSx) - : nameof(AVectorHelper.VectorExtractIntZx)); - } - - public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size) - { - ThrowIfInvalidF(Index, Size); - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorExtractSingle)); - } - else if (Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorExtractDouble)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - } - - public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd) - { - EmitVectorZeroLower(Context, Rd); - EmitVectorZeroUpper(Context, Rd); - } - - public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd) - { - EmitVectorInsert(Context, Rd, 0, 3, 0); - } - - public static void EmitVectorZeroLowerTmp(AILEmitterCtx Context) - { - EmitVectorInsertTmp(Context, 0, 3, 0); - } - - public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd) - { - EmitVectorInsert(Context, Rd, 1, 3, 0); - } - - public static void EmitVectorZero32_128(AILEmitterCtx Context, int Reg) - { - Context.EmitLdvec(Reg); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorZero32_128)); - - Context.EmitStvec(Reg); - } - - public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); - - Context.EmitStvec(Reg); - } - - public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdvectmp(); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); - - Context.EmitStvectmp(); - } - - public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdc_I8(Value); - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); - - Context.EmitStvec(Reg); - } - - public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size, long Value) - { - ThrowIfInvalid(Index, Size); - - Context.EmitLdc_I8(Value); - Context.EmitLdvectmp(); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); - - Context.EmitStvectmp(); - } - - public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size) - { - ThrowIfInvalidF(Index, Size); - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertSingle)); - } - else if (Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertDouble)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitStvec(Reg); - } - - public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size) - { - ThrowIfInvalidF(Index, Size); - - Context.EmitLdvectmp(); - Context.EmitLdc_I4(Index); - - if (Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertSingle)); - } - else if (Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertDouble)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitStvectmp(); - } - - private static void ThrowIfInvalid(int Index, int Size) - { - if ((uint)Size > 3) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - if ((uint)Index >= 16 >> Size) - { - throw new ArgumentOutOfRangeException(nameof(Index)); - } - } - - private static void ThrowIfInvalidF(int Index, int Size) - { - if ((uint)Size > 1) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - if ((uint)Index >= 4 >> Size) - { - throw new ArgumentOutOfRangeException(nameof(Index)); - } - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs deleted file mode 100644 index 9f5af96cb4..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs +++ /dev/null @@ -1,221 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; -using System.Runtime.Intrinsics.X86; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void And_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Call(Context, nameof(Sse2.And)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.And)); - } - } - - public static void Bic_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.And); - }); - } - - public static void Bic_Vi(AILEmitterCtx Context) - { - EmitVectorImmBinaryOp(Context, () => - { - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.And); - }); - } - - public static void Bif_V(AILEmitterCtx Context) - { - EmitBitBif(Context, true); - } - - public static void Bit_V(AILEmitterCtx Context) - { - EmitBitBif(Context, false); - } - - private static void EmitBitBif(AILEmitterCtx Context, bool NotRm) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.Emit(OpCodes.Xor); - - EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size); - - if (NotRm) - { - Context.Emit(OpCodes.Not); - } - - Context.Emit(OpCodes.And); - - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - - Context.Emit(OpCodes.Xor); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Bsl_V(AILEmitterCtx Context) - { - EmitVectorTernaryOpZx(Context, () => - { - Context.EmitSttmp(); - Context.EmitLdtmp(); - - Context.Emit(OpCodes.Xor); - Context.Emit(OpCodes.And); - - Context.EmitLdtmp(); - - Context.Emit(OpCodes.Xor); - }); - } - - public static void Eor_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Call(Context, nameof(Sse2.Xor)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Xor)); - } - } - - public static void Not_V(AILEmitterCtx Context) - { - EmitVectorUnaryOpZx(Context, () => Context.Emit(OpCodes.Not)); - } - - public static void Orn_V(AILEmitterCtx Context) - { - EmitVectorBinaryOpZx(Context, () => - { - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.Or); - }); - } - - public static void Orr_V(AILEmitterCtx Context) - { - if (AOptimizations.UseSse2) - { - EmitSse2Call(Context, nameof(Sse2.Or)); - } - else - { - EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Or)); - } - } - - public static void Orr_Vi(AILEmitterCtx Context) - { - EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or)); - } - - public static void Rbit_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 16 : 8; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, 0); - - Context.Emit(OpCodes.Conv_U4); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBits8)); - - Context.Emit(OpCodes.Conv_U8); - - EmitVectorInsert(Context, Op.Rd, Index, 0); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Rev16_V(AILEmitterCtx Context) - { - EmitRev_V(Context, ContainerSize: 1); - } - - public static void Rev32_V(AILEmitterCtx Context) - { - EmitRev_V(Context, ContainerSize: 2); - } - - public static void Rev64_V(AILEmitterCtx Context) - { - EmitRev_V(Context, ContainerSize: 3); - } - - private static void EmitRev_V(AILEmitterCtx Context, int ContainerSize) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - if (Op.Size >= ContainerSize) - { - throw new InvalidOperationException(); - } - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - int ContainerMask = (1 << (ContainerSize - Op.Size)) - 1; - - for (int Index = 0; Index < Elems; Index++) - { - int RevIndex = Index ^ ContainerMask; - - EmitVectorExtractZx(Context, Op.Rn, RevIndex, Op.Size); - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSimdMemory.cs b/ChocolArm64/Instruction/AInstEmitSimdMemory.cs deleted file mode 100644 index 368b014fba..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdMemory.cs +++ /dev/null @@ -1,185 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; - -using static ChocolArm64.Instruction.AInstEmitMemoryHelper; -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Ld__Vms(AILEmitterCtx Context) - { - EmitSimdMemMs(Context, IsLoad: true); - } - - public static void Ld__Vss(AILEmitterCtx Context) - { - EmitSimdMemSs(Context, IsLoad: true); - } - - public static void St__Vms(AILEmitterCtx Context) - { - EmitSimdMemMs(Context, IsLoad: false); - } - - public static void St__Vss(AILEmitterCtx Context) - { - EmitSimdMemSs(Context, IsLoad: false); - } - - private static void EmitSimdMemMs(AILEmitterCtx Context, bool IsLoad) - { - AOpCodeSimdMemMs Op = (AOpCodeSimdMemMs)Context.CurrOp; - - int Offset = 0; - - for (int Rep = 0; Rep < Op.Reps; Rep++) - for (int Elem = 0; Elem < Op.Elems; Elem++) - for (int SElem = 0; SElem < Op.SElems; SElem++) - { - int Rtt = (Op.Rt + Rep + SElem) & 0x1f; - - if (IsLoad) - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdc_I8(Offset); - - Context.Emit(OpCodes.Add); - - EmitReadZxCall(Context, Op.Size); - - EmitVectorInsert(Context, Rtt, Elem, Op.Size); - - if (Op.RegisterSize == ARegisterSize.SIMD64 && Elem == Op.Elems - 1) - { - EmitVectorZeroUpper(Context, Rtt); - } - } - else - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdc_I8(Offset); - - Context.Emit(OpCodes.Add); - - EmitVectorExtractZx(Context, Rtt, Elem, Op.Size); - - EmitWriteCall(Context, Op.Size); - } - - Offset += 1 << Op.Size; - } - - if (Op.WBack) - { - EmitSimdMemWBack(Context, Offset); - } - } - - private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad) - { - AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp; - - int Offset = 0; - - void EmitMemAddress() - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdc_I8(Offset); - - Context.Emit(OpCodes.Add); - } - - if (Op.Replicate) - { - //Only loads uses the replicate mode. - if (!IsLoad) - { - throw new InvalidOperationException(); - } - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int SElem = 0; SElem < Op.SElems; SElem++) - { - int Rt = (Op.Rt + SElem) & 0x1f; - - for (int Index = 0; Index < Elems; Index++) - { - EmitMemAddress(); - - EmitReadZxCall(Context, Op.Size); - - EmitVectorInsert(Context, Rt, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Rt); - } - - Offset += 1 << Op.Size; - } - } - else - { - for (int SElem = 0; SElem < Op.SElems; SElem++) - { - int Rt = (Op.Rt + SElem) & 0x1f; - - if (IsLoad) - { - EmitMemAddress(); - - EmitReadZxCall(Context, Op.Size); - - EmitVectorInsert(Context, Rt, Op.Index, Op.Size); - } - else - { - EmitMemAddress(); - - EmitVectorExtractZx(Context, Rt, Op.Index, Op.Size); - - EmitWriteCall(Context, Op.Size); - } - - Offset += 1 << Op.Size; - } - } - - if (Op.WBack) - { - EmitSimdMemWBack(Context, Offset); - } - } - - private static void EmitSimdMemWBack(AILEmitterCtx Context, int Offset) - { - AOpCodeMemReg Op = (AOpCodeMemReg)Context.CurrOp; - - Context.EmitLdint(Op.Rn); - - if (Op.Rm != AThreadState.ZRIndex) - { - Context.EmitLdint(Op.Rm); - } - else - { - Context.EmitLdc_I8(Offset); - } - - Context.Emit(OpCodes.Add); - - Context.EmitStint(Op.Rn); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs deleted file mode 100644 index 3bf1e4635b..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs +++ /dev/null @@ -1,422 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Dup_Gp(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; Index++) - { - Context.EmitLdintzr(Op.Rn); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Dup_S(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); - - EmitScalarSet(Context, Op.Rd, Op.Size); - } - - public static void Dup_V(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Ext_V(AILEmitterCtx Context) - { - AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - - int Bytes = Op.GetBitsCount() >> 3; - - int Position = Op.Imm4; - - for (int Index = 0; Index < Bytes; Index++) - { - int Reg = Op.Imm4 + Index < Bytes ? Op.Rn : Op.Rm; - - if (Position == Bytes) - { - Position = 0; - } - - EmitVectorExtractZx(Context, Reg, Position++, 0); - EmitVectorInsertTmp(Context, Index, 0); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Fcsel_S(AILEmitterCtx Context) - { - AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.EmitCondBranch(LblTrue, Op.Cond); - - EmitVectorExtractF(Context, Op.Rm, 0, Op.Size); - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - Context.MarkLabel(LblEnd); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Fmov_Ftoi(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, 0, 3); - - EmitIntZeroUpperIfNeeded(Context); - - Context.EmitStintzr(Op.Rd); - } - - public static void Fmov_Ftoi1(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, 1, 3); - - EmitIntZeroUpperIfNeeded(Context); - - Context.EmitStintzr(Op.Rd); - } - - public static void Fmov_Itof(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - EmitIntZeroUpperIfNeeded(Context); - - EmitScalarSet(Context, Op.Rd, 3); - } - - public static void Fmov_Itof1(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - EmitIntZeroUpperIfNeeded(Context); - - EmitVectorInsert(Context, Op.Rd, 1, 3); - } - - public static void Fmov_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - EmitScalarSetF(Context, Op.Rd, Op.Size); - } - - public static void Fmov_Si(AILEmitterCtx Context) - { - AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp; - - Context.EmitLdc_I8(Op.Imm); - - EmitScalarSet(Context, Op.Rd, Op.Size + 2); - } - - public static void Fmov_V(AILEmitterCtx Context) - { - AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; - - int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 4 : 2; - - for (int Index = 0; Index < (Elems >> Op.Size); Index++) - { - Context.EmitLdc_I8(Op.Imm); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size + 2); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Ins_Gp(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size); - } - - public static void Ins_V(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, Op.SrcIndex, Op.Size); - - EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size); - } - - public static void Movi_V(AILEmitterCtx Context) - { - EmitVectorImmUnaryOp(Context, () => { }); - } - - public static void Mvni_V(AILEmitterCtx Context) - { - EmitVectorImmUnaryOp(Context, () => Context.Emit(OpCodes.Not)); - } - - public static void Tbl_V(AILEmitterCtx Context) - { - AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp; - - Context.EmitLdvec(Op.Rm); - - for (int Index = 0; Index < Op.Size; Index++) - { - Context.EmitLdvec((Op.Rn + Index) & 0x1f); - } - - switch (Op.Size) - { - case 1: AVectorHelper.EmitCall(Context, - nameof(AVectorHelper.Tbl1_V64), - nameof(AVectorHelper.Tbl1_V128)); break; - - case 2: AVectorHelper.EmitCall(Context, - nameof(AVectorHelper.Tbl2_V64), - nameof(AVectorHelper.Tbl2_V128)); break; - - case 3: AVectorHelper.EmitCall(Context, - nameof(AVectorHelper.Tbl3_V64), - nameof(AVectorHelper.Tbl3_V128)); break; - - case 4: AVectorHelper.EmitCall(Context, - nameof(AVectorHelper.Tbl4_V64), - nameof(AVectorHelper.Tbl4_V128)); break; - - default: throw new InvalidOperationException(); - } - - Context.EmitStvec(Op.Rd); - } - - public static void Trn1_V(AILEmitterCtx Context) - { - EmitVectorTranspose(Context, Part: 0); - } - - public static void Trn2_V(AILEmitterCtx Context) - { - EmitVectorTranspose(Context, Part: 1); - } - - public static void Umov_S(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); - - Context.EmitStintzr(Op.Rd); - } - - public static void Uzp1_V(AILEmitterCtx Context) - { - EmitVectorUnzip(Context, Part: 0); - } - - public static void Uzp2_V(AILEmitterCtx Context) - { - EmitVectorUnzip(Context, Part: 1); - } - - public static void Xtn_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Elems = 8 >> Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Zip1_V(AILEmitterCtx Context) - { - EmitVectorZip(Context, Part: 0); - } - - public static void Zip2_V(AILEmitterCtx Context) - { - EmitVectorZip(Context, Part: 1); - } - - private static void EmitIntZeroUpperIfNeeded(AILEmitterCtx Context) - { - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U4); - Context.Emit(OpCodes.Conv_U8); - } - } - - private static void EmitVectorTranspose(AILEmitterCtx Context, int Part) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtractZx(Context, Op.Rn, Idx + Part, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Idx + Part, Op.Size); - - EmitVectorInsertTmp(Context, Idx + 1, Op.Size); - EmitVectorInsertTmp(Context, Idx, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorUnzip(AILEmitterCtx Context, int Part) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtractZx(Context, Op.Rn, Idx + Part, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Idx + Part, Op.Size); - - EmitVectorInsertTmp(Context, Pairs + Index, Op.Size); - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorZip(AILEmitterCtx Context, int Part) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - int Base = Part != 0 ? Pairs : 0; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtractZx(Context, Op.Rn, Base + Index, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Base + Index, Op.Size); - - EmitVectorInsertTmp(Context, Idx + 1, Op.Size); - EmitVectorInsertTmp(Context, Idx, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs deleted file mode 100644 index 4dee53b9b1..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSimdShift.cs +++ /dev/null @@ -1,458 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; - -using static ChocolArm64.Instruction.AInstEmitSimdHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Shl_S(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitScalarUnaryOpZx(Context, () => - { - Context.EmitLdc_I4(GetImmShl(Op)); - - Context.Emit(OpCodes.Shl); - }); - } - - public static void Shl_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorUnaryOpZx(Context, () => - { - Context.EmitLdc_I4(GetImmShl(Op)); - - Context.Emit(OpCodes.Shl); - }); - } - - public static void Shll_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Shift = 8 << Op.Size; - - EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); - } - - public static void Shrn_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), GetImmShr(Op)); - } - - public static void Sli_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - int Shift = GetImmShl(Op); - - ulong Mask = Shift != 0 ? ulong.MaxValue >> (64 - Shift) : 0; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.EmitLdc_I4(Shift); - - Context.Emit(OpCodes.Shl); - - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - - Context.EmitLdc_I8((long)Mask); - - Context.Emit(OpCodes.And); - Context.Emit(OpCodes.Or); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - public static void Sqrshrn_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - int Shift = GetImmShr(Op); - - long RoundConst = 1L << (Shift - 1); - - Action Emit = () => - { - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - - Context.EmitLdc_I4(Shift); - - Context.Emit(OpCodes.Shr); - }; - - EmitVectorSaturatingNarrowOpSxSx(Context, Emit); - } - - public static void Srshr_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpSx(Context, ShrImmFlags.Round); - } - - public static void Srshr_V(AILEmitterCtx Context) - { - EmitVectorShrImmOpSx(Context, ShrImmFlags.Round); - } - - public static void Srsra_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpSx(Context, ShrImmFlags.Round | ShrImmFlags.Accumulate); - } - - public static void Srsra_V(AILEmitterCtx Context) - { - EmitVectorShrImmOpSx(Context, ShrImmFlags.Round | ShrImmFlags.Accumulate); - } - - public static void Sshl_V(AILEmitterCtx Context) - { - EmitVectorShl(Context, Signed: true); - } - - public static void Sshll_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorShImmWidenBinarySx(Context, () => Context.Emit(OpCodes.Shl), GetImmShl(Op)); - } - - public static void Sshr_S(AILEmitterCtx Context) - { - EmitShrImmOp(Context, ShrImmFlags.ScalarSx); - } - - public static void Sshr_V(AILEmitterCtx Context) - { - EmitShrImmOp(Context, ShrImmFlags.VectorSx); - } - - public static void Ssra_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpSx(Context, ShrImmFlags.Accumulate); - } - - public static void Ssra_V(AILEmitterCtx Context) - { - EmitVectorShrImmOpSx(Context, ShrImmFlags.Accumulate); - } - - public static void Urshr_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpZx(Context, ShrImmFlags.Round); - } - - public static void Urshr_V(AILEmitterCtx Context) - { - EmitVectorShrImmOpZx(Context, ShrImmFlags.Round); - } - - public static void Ursra_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpZx(Context, ShrImmFlags.Round | ShrImmFlags.Accumulate); - } - - public static void Ursra_V(AILEmitterCtx Context) - { - EmitVectorShrImmOpZx(Context, ShrImmFlags.Round | ShrImmFlags.Accumulate); - } - - public static void Ushl_V(AILEmitterCtx Context) - { - EmitVectorShl(Context, Signed: false); - } - - public static void Ushll_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), GetImmShl(Op)); - } - - public static void Ushr_S(AILEmitterCtx Context) - { - EmitShrImmOp(Context, ShrImmFlags.ScalarZx); - } - - public static void Ushr_V(AILEmitterCtx Context) - { - EmitShrImmOp(Context, ShrImmFlags.VectorZx); - } - - public static void Usra_S(AILEmitterCtx Context) - { - EmitScalarShrImmOpZx(Context, ShrImmFlags.Accumulate); - } - - public static void Usra_V(AILEmitterCtx Context) - { - EmitVectorShrImmOpZx(Context, ShrImmFlags.Accumulate); - } - - private static void EmitVectorShl(AILEmitterCtx Context, bool Signed) - { - //This instruction shifts the value on vector A by the number of bits - //specified on the signed, lower 8 bits of vector B. If the shift value - //is greater or equal to the data size of each lane, then the result is zero. - //Additionally, negative shifts produces right shifts by the negated shift value. - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int MaxShift = 8 << Op.Size; - - Action Emit = () => - { - AILLabel LblShl = new AILLabel(); - AILLabel LblZero = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - void EmitShift(OpCode ILOp) - { - Context.Emit(OpCodes.Dup); - - Context.EmitLdc_I4(MaxShift); - - Context.Emit(OpCodes.Bge_S, LblZero); - Context.Emit(ILOp); - Context.Emit(OpCodes.Br_S, LblEnd); - } - - Context.Emit(OpCodes.Conv_I1); - Context.Emit(OpCodes.Dup); - - Context.EmitLdc_I4(0); - - Context.Emit(OpCodes.Bge_S, LblShl); - Context.Emit(OpCodes.Neg); - - EmitShift(Signed - ? OpCodes.Shr - : OpCodes.Shr_Un); - - Context.MarkLabel(LblShl); - - EmitShift(OpCodes.Shl); - - Context.MarkLabel(LblZero); - - Context.Emit(OpCodes.Pop); - Context.Emit(OpCodes.Pop); - - Context.EmitLdc_I8(0); - - Context.MarkLabel(LblEnd); - }; - - if (Signed) - { - EmitVectorBinaryOpSx(Context, Emit); - } - else - { - EmitVectorBinaryOpZx(Context, Emit); - } - } - - [Flags] - private enum ShrImmFlags - { - Scalar = 1 << 0, - Signed = 1 << 1, - - Round = 1 << 2, - Accumulate = 1 << 3, - - ScalarSx = Scalar | Signed, - ScalarZx = Scalar, - - VectorSx = Signed, - VectorZx = 0 - } - - private static void EmitScalarShrImmOpSx(AILEmitterCtx Context, ShrImmFlags Flags) - { - EmitShrImmOp(Context, ShrImmFlags.ScalarSx | Flags); - } - - private static void EmitScalarShrImmOpZx(AILEmitterCtx Context, ShrImmFlags Flags) - { - EmitShrImmOp(Context, ShrImmFlags.ScalarZx | Flags); - } - - private static void EmitVectorShrImmOpSx(AILEmitterCtx Context, ShrImmFlags Flags) - { - EmitShrImmOp(Context, ShrImmFlags.VectorSx | Flags); - } - - private static void EmitVectorShrImmOpZx(AILEmitterCtx Context, ShrImmFlags Flags) - { - EmitShrImmOp(Context, ShrImmFlags.VectorZx | Flags); - } - - private static void EmitShrImmOp(AILEmitterCtx Context, ShrImmFlags Flags) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - bool Scalar = (Flags & ShrImmFlags.Scalar) != 0; - bool Signed = (Flags & ShrImmFlags.Signed) != 0; - bool Round = (Flags & ShrImmFlags.Round) != 0; - bool Accumulate = (Flags & ShrImmFlags.Accumulate) != 0; - - int Shift = GetImmShr(Op); - - long RoundConst = 1L << (Shift - 1); - - int Bytes = Op.GetBitsCount() >> 3; - int Elems = !Scalar ? Bytes >> Op.Size : 1; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - - if (Op.Size <= 2) - { - if (Round) - { - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - } - - Context.EmitLdc_I4(Shift); - - Context.Emit(Signed ? OpCodes.Shr : OpCodes.Shr_Un); - } - else /* if (Op.Size == 3) */ - { - EmitShrImm_64(Context, Signed, Round ? RoundConst : 0L, Shift); - } - - if (Accumulate) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - - Context.Emit(OpCodes.Add); - } - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - // Dst_64 = (Int(Src_64, Signed) + RoundConst) >> Shift; - private static void EmitShrImm_64( - AILEmitterCtx Context, - bool Signed, - long RoundConst, - int Shift) - { - if (((AOpCodeSimd)Context.CurrOp).Size < 3) - { - throw new InvalidOperationException(); - } - - Context.EmitLdc_I8(RoundConst); - Context.EmitLdc_I4(Shift); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.SignedShrImm_64) - : nameof(ASoftFallback.UnsignedShrImm_64)); - } - - private static void EmitVectorShImmNarrowBinarySx(AILEmitterCtx Context, Action Emit, int Imm) - { - EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, true); - } - - private static void EmitVectorShImmNarrowBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) - { - EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, false); - } - - private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Elems = 8 >> Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); - - Context.EmitLdc_I4(Imm); - - Emit(); - - EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); - } - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorShImmWidenBinarySx(AILEmitterCtx Context, Action Emit, int Imm) - { - EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, true); - } - - private static void EmitVectorShImmWidenBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) - { - EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, false); - } - - private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Elems = 8 >> Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); - - Context.EmitLdc_I4(Imm); - - Emit(); - - EmitVectorInsertTmp(Context, Index, Op.Size + 1); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - } - } -} diff --git a/ChocolArm64/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs deleted file mode 100644 index 1c5d02634f..0000000000 --- a/ChocolArm64/Instruction/AInstEmitSystem.cs +++ /dev/null @@ -1,133 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Hint(AILEmitterCtx Context) - { - //Execute as no-op. - } - - public static void Mrs(AILEmitterCtx Context) - { - AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - - string PropName; - - switch (GetPackedId(Op)) - { - case 0b11_011_0000_0000_001: PropName = nameof(AThreadState.CtrEl0); break; - case 0b11_011_0000_0000_111: PropName = nameof(AThreadState.DczidEl0); break; - case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break; - case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; - case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; - case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break; - case 0b11_011_1110_0000_000: PropName = nameof(AThreadState.CntfrqEl0); break; - case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break; - - default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}"); - } - - Context.EmitCallPropGet(typeof(AThreadState), PropName); - - PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName); - - if (PropInfo.PropertyType != typeof(long) && - PropInfo.PropertyType != typeof(ulong)) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.EmitStintzr(Op.Rt); - } - - public static void Msr(AILEmitterCtx Context) - { - AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; - - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitLdintzr(Op.Rt); - - string PropName; - - switch (GetPackedId(Op)) - { - case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break; - case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; - case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; - - default: throw new NotImplementedException($"Unknown MSR at {Op.Position:x16}"); - } - - PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName); - - if (PropInfo.PropertyType != typeof(long) && - PropInfo.PropertyType != typeof(ulong)) - { - Context.Emit(OpCodes.Conv_U4); - } - - Context.EmitCallPropSet(typeof(AThreadState), PropName); - } - - public static void Nop(AILEmitterCtx Context) - { - //Do nothing. - } - - public static void Sys(AILEmitterCtx Context) - { - //This instruction is used to do some operations on the CPU like cache invalidation, - //address translation and the like. - //We treat it as no-op here since we don't have any cache being emulated anyway. - AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; - - switch (GetPackedId(Op)) - { - case 0b11_011_0111_0100_001: - { - //DC ZVA - for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8) - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdintzr(Op.Rt); - Context.EmitLdc_I(Offs); - - Context.Emit(OpCodes.Add); - - Context.EmitLdc_I8(0); - - AInstEmitMemoryHelper.EmitWriteCall(Context, 3); - } - - break; - } - - //No-op - case 0b11_011_0111_1110_001: //DC CIVAC - break; - } - } - - private static int GetPackedId(AOpCodeSystem Op) - { - int Id; - - Id = Op.Op2 << 0; - Id |= Op.CRm << 3; - Id |= Op.CRn << 7; - Id |= Op.Op1 << 11; - Id |= Op.Op0 << 14; - - return Id; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitter.cs b/ChocolArm64/Instruction/AInstEmitter.cs deleted file mode 100644 index 8712a7367c..0000000000 --- a/ChocolArm64/Instruction/AInstEmitter.cs +++ /dev/null @@ -1,6 +0,0 @@ -using ChocolArm64.Translation; - -namespace ChocolArm64.Instruction -{ - delegate void AInstEmitter(AILEmitterCtx Context); -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstInterpreter.cs b/ChocolArm64/Instruction/AInstInterpreter.cs deleted file mode 100644 index 6a855aecb0..0000000000 --- a/ChocolArm64/Instruction/AInstInterpreter.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Memory; -using ChocolArm64.State; - -namespace ChocolArm64.Instruction -{ - delegate void AInstInterpreter(AThreadState State, AMemory Memory, AOpCode OpCode); -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs deleted file mode 100644 index a7bc108591..0000000000 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ /dev/null @@ -1,782 +0,0 @@ -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace ChocolArm64.Instruction -{ - using static AVectorHelper; - - static class ASoftFallback - { - public static void EmitCall(AILEmitterCtx Context, string MthdName) - { - Context.EmitCall(typeof(ASoftFallback), MthdName); - } - -#region "ShrImm_64" - public static long SignedShrImm_64(long Value, long RoundConst, int Shift) - { - if (RoundConst == 0L) - { - if (Shift <= 63) - { - return Value >> Shift; - } - else /* if (Shift == 64) */ - { - if (Value < 0L) - { - return -1L; - } - else - { - return 0L; - } - } - } - else /* if (RoundConst == 1L << (Shift - 1)) */ - { - if (Shift <= 63) - { - long Add = Value + RoundConst; - - if ((~Value & (Value ^ Add)) < 0L) - { - return (long)((ulong)Add >> Shift); - } - else - { - return Add >> Shift; - } - } - else /* if (Shift == 64) */ - { - return 0L; - } - } - } - - public static ulong UnsignedShrImm_64(ulong Value, long RoundConst, int Shift) - { - if (RoundConst == 0L) - { - if (Shift <= 63) - { - return Value >> Shift; - } - else /* if (Shift == 64) */ - { - return 0UL; - } - } - else /* if (RoundConst == 1L << (Shift - 1)) */ - { - ulong Add = Value + (ulong)RoundConst; - - if ((Add < Value) && (Add < (ulong)RoundConst)) - { - if (Shift <= 63) - { - return (Add >> Shift) | (0x8000000000000000UL >> (Shift - 1)); - } - else /* if (Shift == 64) */ - { - return 1UL; - } - } - else - { - if (Shift <= 63) - { - return Add >> Shift; - } - else /* if (Shift == 64) */ - { - return 0UL; - } - } - } - } -#endregion - -#region "Saturating" - public static long SignedSrcSignedDstSatQ(long op, int Size, AThreadState State) - { - int ESize = 8 << Size; - - long TMaxValue = (1L << (ESize - 1)) - 1L; - long TMinValue = -(1L << (ESize - 1)); - - if (op > TMaxValue) - { - SetFpsrQCFlag(State); - - return TMaxValue; - } - else if (op < TMinValue) - { - SetFpsrQCFlag(State); - - return TMinValue; - } - else - { - return op; - } - } - - public static ulong SignedSrcUnsignedDstSatQ(long op, int Size, AThreadState State) - { - int ESize = 8 << Size; - - ulong TMaxValue = (1UL << ESize) - 1UL; - ulong TMinValue = 0UL; - - if (op > (long)TMaxValue) - { - SetFpsrQCFlag(State); - - return TMaxValue; - } - else if (op < (long)TMinValue) - { - SetFpsrQCFlag(State); - - return TMinValue; - } - else - { - return (ulong)op; - } - } - - public static long UnsignedSrcSignedDstSatQ(ulong op, int Size, AThreadState State) - { - int ESize = 8 << Size; - - long TMaxValue = (1L << (ESize - 1)) - 1L; - - if (op > (ulong)TMaxValue) - { - SetFpsrQCFlag(State); - - return TMaxValue; - } - else - { - return (long)op; - } - } - - public static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int Size, AThreadState State) - { - int ESize = 8 << Size; - - ulong TMaxValue = (1UL << ESize) - 1UL; - - if (op > TMaxValue) - { - SetFpsrQCFlag(State); - - return TMaxValue; - } - else - { - return op; - } - } - - public static long UnarySignedSatQAbsOrNeg(long op, AThreadState State) - { - if (op == long.MinValue) - { - SetFpsrQCFlag(State); - - return long.MaxValue; - } - else - { - return op; - } - } - - public static long BinarySignedSatQAdd(long op1, long op2, AThreadState State) - { - long Add = op1 + op2; - - if ((~(op1 ^ op2) & (op1 ^ Add)) < 0L) - { - SetFpsrQCFlag(State); - - if (op1 < 0L) - { - return long.MinValue; - } - else - { - return long.MaxValue; - } - } - else - { - return Add; - } - } - - public static ulong BinaryUnsignedSatQAdd(ulong op1, ulong op2, AThreadState State) - { - ulong Add = op1 + op2; - - if ((Add < op1) && (Add < op2)) - { - SetFpsrQCFlag(State); - - return ulong.MaxValue; - } - else - { - return Add; - } - } - - public static long BinarySignedSatQSub(long op1, long op2, AThreadState State) - { - long Sub = op1 - op2; - - if (((op1 ^ op2) & (op1 ^ Sub)) < 0L) - { - SetFpsrQCFlag(State); - - if (op1 < 0L) - { - return long.MinValue; - } - else - { - return long.MaxValue; - } - } - else - { - return Sub; - } - } - - public static ulong BinaryUnsignedSatQSub(ulong op1, ulong op2, AThreadState State) - { - ulong Sub = op1 - op2; - - if (op1 < op2) - { - SetFpsrQCFlag(State); - - return ulong.MinValue; - } - else - { - return Sub; - } - } - - public static long BinarySignedSatQAcc(ulong op1, long op2, AThreadState State) - { - if (op1 <= (ulong)long.MaxValue) - { - // op1 from ulong.MinValue to (ulong)long.MaxValue - // op2 from long.MinValue to long.MaxValue - - long Add = (long)op1 + op2; - - if ((~op2 & Add) < 0L) - { - SetFpsrQCFlag(State); - - return long.MaxValue; - } - else - { - return Add; - } - } - else if (op2 >= 0L) - { - // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue - // op2 from (long)ulong.MinValue to long.MaxValue - - SetFpsrQCFlag(State); - - return long.MaxValue; - } - else - { - // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue - // op2 from long.MinValue to (long)ulong.MinValue - 1L - - ulong Add = op1 + (ulong)op2; - - if (Add > (ulong)long.MaxValue) - { - SetFpsrQCFlag(State); - - return long.MaxValue; - } - else - { - return (long)Add; - } - } - } - - public static ulong BinaryUnsignedSatQAcc(long op1, ulong op2, AThreadState State) - { - if (op1 >= 0L) - { - // op1 from (long)ulong.MinValue to long.MaxValue - // op2 from ulong.MinValue to ulong.MaxValue - - ulong Add = (ulong)op1 + op2; - - if ((Add < (ulong)op1) && (Add < op2)) - { - SetFpsrQCFlag(State); - - return ulong.MaxValue; - } - else - { - return Add; - } - } - else if (op2 > (ulong)long.MaxValue) - { - // op1 from long.MinValue to (long)ulong.MinValue - 1L - // op2 from (ulong)long.MaxValue + 1UL to ulong.MaxValue - - return (ulong)op1 + op2; - } - else - { - // op1 from long.MinValue to (long)ulong.MinValue - 1L - // op2 from ulong.MinValue to (ulong)long.MaxValue - - long Add = op1 + (long)op2; - - if (Add < (long)ulong.MinValue) - { - SetFpsrQCFlag(State); - - return ulong.MinValue; - } - else - { - return (ulong)Add; - } - } - } - - private static void SetFpsrQCFlag(AThreadState State) - { - const int QCFlagBit = 27; - - State.Fpsr |= 1 << QCFlagBit; - } -#endregion - -#region "Count" - public static ulong CountLeadingSigns(ulong Value, int Size) - { - Value ^= Value >> 1; - - int HighBit = Size - 2; - - for (int Bit = HighBit; Bit >= 0; Bit--) - { - if (((Value >> Bit) & 0b1) != 0) - { - return (ulong)(HighBit - Bit); - } - } - - return (ulong)(Size - 1); - } - - private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; - - public static ulong CountLeadingZeros(ulong Value, int Size) - { - if (Value == 0) - { - return (ulong)Size; - } - - int NibbleIdx = Size; - int PreCount, Count = 0; - - do - { - NibbleIdx -= 4; - PreCount = ClzNibbleTbl[(Value >> NibbleIdx) & 0b1111]; - Count += PreCount; - } - while (PreCount == 4); - - return (ulong)Count; - } - - public static uint CountSetBits8(uint Value) - { - Value = ((Value >> 1) & 0x55) + (Value & 0x55); - Value = ((Value >> 2) & 0x33) + (Value & 0x33); - - return (Value >> 4) + (Value & 0x0f); - } -#endregion - -#region "Crc32" - private const uint Crc32RevPoly = 0xedb88320; - private const uint Crc32cRevPoly = 0x82f63b78; - - public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val); - public static uint Crc32h(uint Crc, ushort Val) => Crc32h(Crc, Crc32RevPoly, Val); - public static uint Crc32w(uint Crc, uint Val) => Crc32w(Crc, Crc32RevPoly, Val); - public static uint Crc32x(uint Crc, ulong Val) => Crc32x(Crc, Crc32RevPoly, Val); - - public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val); - public static uint Crc32ch(uint Crc, ushort Val) => Crc32h(Crc, Crc32cRevPoly, Val); - public static uint Crc32cw(uint Crc, uint Val) => Crc32w(Crc, Crc32cRevPoly, Val); - public static uint Crc32cx(uint Crc, ulong Val) => Crc32x(Crc, Crc32cRevPoly, Val); - - private static uint Crc32h(uint Crc, uint Poly, ushort Val) - { - Crc = Crc32(Crc, Poly, (byte)(Val >> 0)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 8)); - - return Crc; - } - - private static uint Crc32w(uint Crc, uint Poly, uint Val) - { - Crc = Crc32(Crc, Poly, (byte)(Val >> 0 )); - Crc = Crc32(Crc, Poly, (byte)(Val >> 8 )); - Crc = Crc32(Crc, Poly, (byte)(Val >> 16)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 24)); - - return Crc; - } - - private static uint Crc32x(uint Crc, uint Poly, ulong Val) - { - Crc = Crc32(Crc, Poly, (byte)(Val >> 0 )); - Crc = Crc32(Crc, Poly, (byte)(Val >> 8 )); - Crc = Crc32(Crc, Poly, (byte)(Val >> 16)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 24)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 32)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 40)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 48)); - Crc = Crc32(Crc, Poly, (byte)(Val >> 56)); - - return Crc; - } - - private static uint Crc32(uint Crc, uint Poly, byte Val) - { - Crc ^= Val; - - for (int Bit = 7; Bit >= 0; Bit--) - { - uint Mask = (uint)(-(int)(Crc & 1)); - - Crc = (Crc >> 1) ^ (Poly & Mask); - } - - return Crc; - } -#endregion - -#region "Aes" - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Decrypt(Vector128 value, Vector128 roundKey) - { - if (!Sse.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - return ACryptoHelper.AESInvSubBytes(ACryptoHelper.AESInvShiftRows(Sse.Xor(value, roundKey))); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Encrypt(Vector128 value, Vector128 roundKey) - { - if (!Sse.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - return ACryptoHelper.AESSubBytes(ACryptoHelper.AESShiftRows(Sse.Xor(value, roundKey))); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 InverseMixColumns(Vector128 value) - { - return ACryptoHelper.AESInvMixColumns(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 MixColumns(Vector128 value) - { - return ACryptoHelper.AESMixColumns(value); - } -#endregion - -#region "Sha256" - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 HashLower(Vector128 hash_abcd, Vector128 hash_efgh, Vector128 wk) - { - return SHA256hash(hash_abcd, hash_efgh, wk, true); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 HashUpper(Vector128 hash_efgh, Vector128 hash_abcd, Vector128 wk) - { - return SHA256hash(hash_abcd, hash_efgh, wk, false); - } - - public static Vector128 SchedulePart1(Vector128 w0_3, Vector128 w4_7) - { - Vector128 result = new Vector128(); - - for (int e = 0; e <= 3; e++) - { - uint elt = (uint)VectorExtractIntZx(e <= 2 ? w0_3 : w4_7, (byte)(e <= 2 ? e + 1 : 0), 2); - - elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3); - - elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); - - result = VectorInsertInt((ulong)elt, result, (byte)e, 2); - } - - return result; - } - - public static Vector128 SchedulePart2(Vector128 w0_3, Vector128 w8_11, Vector128 w12_15) - { - Vector128 result = new Vector128(); - - ulong T1 = VectorExtractIntZx(w12_15, (byte)1, 3); - - for (int e = 0; e <= 1; e++) - { - uint elt = T1.ULongPart(e); - - elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); - - elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); - elt += (uint)VectorExtractIntZx(w8_11, (byte)(e + 1), 2); - - result = VectorInsertInt((ulong)elt, result, (byte)e, 2); - } - - T1 = VectorExtractIntZx(result, (byte)0, 3); - - for (int e = 2; e <= 3; e++) - { - uint elt = T1.ULongPart(e - 2); - - elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); - - elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); - elt += (uint)VectorExtractIntZx(e == 2 ? w8_11 : w12_15, (byte)(e == 2 ? 3 : 0), 2); - - result = VectorInsertInt((ulong)elt, result, (byte)e, 2); - } - - return result; - } - - private static Vector128 SHA256hash(Vector128 X, Vector128 Y, Vector128 W, bool part1) - { - for (int e = 0; e <= 3; e++) - { - uint chs = SHAchoose((uint)VectorExtractIntZx(Y, (byte)0, 2), - (uint)VectorExtractIntZx(Y, (byte)1, 2), - (uint)VectorExtractIntZx(Y, (byte)2, 2)); - - uint maj = SHAmajority((uint)VectorExtractIntZx(X, (byte)0, 2), - (uint)VectorExtractIntZx(X, (byte)1, 2), - (uint)VectorExtractIntZx(X, (byte)2, 2)); - - uint t1 = (uint)VectorExtractIntZx(Y, (byte)3, 2); - t1 += SHAhashSIGMA1((uint)VectorExtractIntZx(Y, (byte)0, 2)) + chs; - t1 += (uint)VectorExtractIntZx(W, (byte)e, 2); - - uint t2 = t1 + (uint)VectorExtractIntZx(X, (byte)3, 2); - X = VectorInsertInt((ulong)t2, X, (byte)3, 2); - t2 = t1 + SHAhashSIGMA0((uint)VectorExtractIntZx(X, (byte)0, 2)) + maj; - Y = VectorInsertInt((ulong)t2, Y, (byte)3, 2); - - Rol32_256(ref Y, ref X); - } - - return part1 ? X : Y; - } - - private static void Rol32_256(ref Vector128 Y, ref Vector128 X) - { - if (!Sse2.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - uint yE3 = (uint)VectorExtractIntZx(Y, (byte)3, 2); - uint xE3 = (uint)VectorExtractIntZx(X, (byte)3, 2); - - Y = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(Y), (byte)4)); - X = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(X), (byte)4)); - - Y = VectorInsertInt((ulong)xE3, Y, (byte)0, 2); - X = VectorInsertInt((ulong)yE3, X, (byte)0, 2); - } - - private static uint SHAhashSIGMA0(uint x) - { - return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22); - } - - private static uint SHAhashSIGMA1(uint x) - { - return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25); - } - - private static uint SHAmajority(uint x, uint y, uint z) - { - return (x & y) | ((x | y) & z); - } - - private static uint SHAchoose(uint x, uint y, uint z) - { - return ((y ^ z) & x) ^ z; - } - - private static uint Ror(this uint value, int count) - { - return (value >> count) | (value << (32 - count)); - } - - private static uint Lsr(this uint value, int count) - { - return value >> count; - } - - private static uint ULongPart(this ulong value, int part) - { - return part == 0 - ? (uint)(value & 0xFFFFFFFFUL) - : (uint)(value >> 32); - } -#endregion - -#region "Reverse" - public static uint ReverseBits8(uint Value) - { - Value = ((Value & 0xaa) >> 1) | ((Value & 0x55) << 1); - Value = ((Value & 0xcc) >> 2) | ((Value & 0x33) << 2); - - return (Value >> 4) | ((Value & 0x0f) << 4); - } - - public static uint ReverseBits32(uint Value) - { - Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1); - Value = ((Value & 0xcccccccc) >> 2) | ((Value & 0x33333333) << 2); - Value = ((Value & 0xf0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f) << 4); - Value = ((Value & 0xff00ff00) >> 8) | ((Value & 0x00ff00ff) << 8); - - return (Value >> 16) | (Value << 16); - } - - public static ulong ReverseBits64(ulong Value) - { - Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((Value & 0x5555555555555555) << 1 ); - Value = ((Value & 0xcccccccccccccccc) >> 2 ) | ((Value & 0x3333333333333333) << 2 ); - Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4 ); - Value = ((Value & 0xff00ff00ff00ff00) >> 8 ) | ((Value & 0x00ff00ff00ff00ff) << 8 ); - Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); - - return (Value >> 32) | (Value << 32); - } - - public static uint ReverseBytes16_32(uint Value) => (uint)ReverseBytes16_64(Value); - public static uint ReverseBytes32_32(uint Value) => (uint)ReverseBytes32_64(Value); - - public static ulong ReverseBytes16_64(ulong Value) => ReverseBytes(Value, RevSize.Rev16); - public static ulong ReverseBytes32_64(ulong Value) => ReverseBytes(Value, RevSize.Rev32); - public static ulong ReverseBytes64(ulong Value) => ReverseBytes(Value, RevSize.Rev64); - - private enum RevSize - { - Rev16, - Rev32, - Rev64 - } - - private static ulong ReverseBytes(ulong Value, RevSize Size) - { - Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); - - if (Size == RevSize.Rev16) - { - return Value; - } - - Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); - - if (Size == RevSize.Rev32) - { - return Value; - } - - Value = ((Value & 0xffffffff00000000) >> 32) | ((Value & 0x00000000ffffffff) << 32); - - if (Size == RevSize.Rev64) - { - return Value; - } - - throw new ArgumentException(nameof(Size)); - } -#endregion - -#region "MultiplyHigh" - public static long SMulHi128(long LHS, long RHS) - { - long Result = (long)UMulHi128((ulong)LHS, (ulong)RHS); - if (LHS < 0) Result -= RHS; - if (RHS < 0) Result -= LHS; - - return Result; - } - - public static ulong UMulHi128(ulong LHS, ulong RHS) - { - //long multiplication - //multiply 32 bits at a time in 64 bit, the result is what's carried over 64 bits. - ulong LHigh = LHS >> 32; - ulong LLow = LHS & 0xFFFFFFFF; - ulong RHigh = RHS >> 32; - ulong RLow = RHS & 0xFFFFFFFF; - ulong Z2 = LLow * RLow; - ulong T = LHigh * RLow + (Z2 >> 32); - ulong Z1 = T & 0xFFFFFFFF; - ulong Z0 = T >> 32; - Z1 += LLow * RHigh; - - return LHigh * RHigh + Z0 + (Z1 >> 32); - } -#endregion - } -} diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs deleted file mode 100644 index e3f067ed50..0000000000 --- a/ChocolArm64/Instruction/ASoftFloat.cs +++ /dev/null @@ -1,537 +0,0 @@ -using System; - -namespace ChocolArm64.Instruction -{ - static class ASoftFloat - { - static ASoftFloat() - { - InvSqrtEstimateTable = BuildInvSqrtEstimateTable(); - RecipEstimateTable = BuildRecipEstimateTable(); - } - - private static readonly byte[] RecipEstimateTable; - private static readonly byte[] InvSqrtEstimateTable; - - private static byte[] BuildInvSqrtEstimateTable() - { - byte[] Table = new byte[512]; - for (ulong index = 128; index < 512; index++) - { - ulong a = index; - if (a < 256) - { - a = (a << 1) + 1; - } - else - { - a = (a | 1) << 1; - } - - ulong b = 256; - while (a * (b + 1) * (b + 1) < (1ul << 28)) - { - b++; - } - b = (b + 1) >> 1; - - Table[index] = (byte)(b & 0xFF); - } - return Table; - } - - private static byte[] BuildRecipEstimateTable() - { - byte[] Table = new byte[256]; - for (ulong index = 0; index < 256; index++) - { - ulong a = index | 0x100; - - a = (a << 1) + 1; - ulong b = 0x80000 / a; - b = (b + 1) >> 1; - - Table[index] = (byte)(b & 0xFF); - } - return Table; - } - - public static float InvSqrtEstimate(float x) - { - return (float)InvSqrtEstimate((double)x); - } - - public static double InvSqrtEstimate(double x) - { - ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x); - ulong x_sign = x_bits & 0x8000000000000000; - long x_exp = (long)((x_bits >> 52) & 0x7FF); - ulong scaled = x_bits & ((1ul << 52) - 1); - - if (x_exp == 0x7FF && scaled != 0) - { - // NaN - return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000)); - } - - if (x_exp == 0) - { - if (scaled == 0) - { - // Zero -> Infinity - return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000)); - } - - // Denormal - while ((scaled & (1 << 51)) == 0) - { - scaled <<= 1; - x_exp--; - } - scaled <<= 1; - } - - if (x_sign != 0) - { - // Negative -> NaN - return BitConverter.Int64BitsToDouble((long)0x7FF8000000000000); - } - - if (x_exp == 0x7ff && scaled == 0) - { - // Infinity -> Zero - return BitConverter.Int64BitsToDouble((long)x_sign); - } - - if (((ulong)x_exp & 1) == 1) - { - scaled >>= 45; - scaled &= 0xFF; - scaled |= 0x80; - } - else - { - scaled >>= 44; - scaled &= 0xFF; - scaled |= 0x100; - } - - ulong result_exp = ((ulong)(3068 - x_exp) / 2) & 0x7FF; - ulong estimate = (ulong)InvSqrtEstimateTable[scaled]; - ulong fraction = estimate << 44; - - ulong result = x_sign | (result_exp << 52) | fraction; - return BitConverter.Int64BitsToDouble((long)result); - } - - public static float RecipEstimate(float x) - { - return (float)RecipEstimate((double)x); - } - - public static double RecipEstimate(double x) - { - ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x); - ulong x_sign = x_bits & 0x8000000000000000; - ulong x_exp = (x_bits >> 52) & 0x7FF; - ulong scaled = x_bits & ((1ul << 52) - 1); - - if (x_exp >= 2045) - { - if (x_exp == 0x7ff && scaled != 0) - { - // NaN - return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000)); - } - - // Infinity, or Out of range -> Zero - return BitConverter.Int64BitsToDouble((long)x_sign); - } - - if (x_exp == 0) - { - if (scaled == 0) - { - // Zero -> Infinity - return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000)); - } - - // Denormal - if ((scaled & (1ul << 51)) == 0) - { - x_exp = ~0ul; - scaled <<= 2; - } - else - { - scaled <<= 1; - } - } - - scaled >>= 44; - scaled &= 0xFF; - - ulong result_exp = (2045 - x_exp) & 0x7FF; - ulong estimate = (ulong)RecipEstimateTable[scaled]; - ulong fraction = estimate << 44; - - if (result_exp == 0) - { - fraction >>= 1; - fraction |= 1ul << 51; - } - else if (result_exp == 0x7FF) - { - result_exp = 0; - fraction >>= 2; - fraction |= 1ul << 50; - } - - ulong result = x_sign | (result_exp << 52) | fraction; - return BitConverter.Int64BitsToDouble((long)result); - } - - public static float RecipStep(float op1, float op2) - { - return (float)RecipStep((double)op1, (double)op2); - } - - public static double RecipStep(double op1, double op2) - { - op1 = -op1; - - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); - - ulong op1_sign = op1_bits & 0x8000000000000000; - ulong op2_sign = op2_bits & 0x8000000000000000; - ulong op1_other = op1_bits & 0x7FFFFFFFFFFFFFFF; - ulong op2_other = op2_bits & 0x7FFFFFFFFFFFFFFF; - - bool inf1 = op1_other == 0x7FF0000000000000; - bool inf2 = op2_other == 0x7FF0000000000000; - bool zero1 = op1_other == 0; - bool zero2 = op2_other == 0; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - return 2.0; - } - else if (inf1 || inf2) - { - // Infinity - return BitConverter.Int64BitsToDouble((long)(0x7FF0000000000000 | (op1_sign ^ op2_sign))); - } - - return 2.0 + op1 * op2; - } - - public static float ConvertHalfToSingle(ushort x) - { - uint x_sign = (uint)(x >> 15) & 0x0001; - uint x_exp = (uint)(x >> 10) & 0x001F; - uint x_mantissa = (uint)x & 0x03FF; - - if (x_exp == 0 && x_mantissa == 0) - { - // Zero - return BitConverter.Int32BitsToSingle((int)(x_sign << 31)); - } - - if (x_exp == 0x1F) - { - // NaN or Infinity - return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | 0x7F800000 | (x_mantissa << 13))); - } - - int exponent = (int)x_exp - 15; - - if (x_exp == 0) - { - // Denormal - x_mantissa <<= 1; - while ((x_mantissa & 0x0400) == 0) - { - x_mantissa <<= 1; - exponent--; - } - x_mantissa &= 0x03FF; - } - - uint new_exp = (uint)((exponent + 127) & 0xFF) << 23; - return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | new_exp | (x_mantissa << 13))); - } - - public static float MaxNum(float op1, float op2) - { - uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); - uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); - - if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) - { - op1 = float.NegativeInfinity; - } - else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) - { - op2 = float.NegativeInfinity; - } - - return Max(op1, op2); - } - - public static double MaxNum(double op1, double op2) - { - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); - - if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) - { - op1 = double.NegativeInfinity; - } - else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) - { - op2 = double.NegativeInfinity; - } - - return Max(op1, op2); - } - - public static float Max(float op1, float op2) - { - // Fast path - if (op1 > op2) - { - return op1; - } - - if (op1 < op2 || (op1 == op2 && op2 != 0)) - { - return op2; - } - - uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); - uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); - - // Handle NaN cases - if (ProcessNaNs(op1_bits, op2_bits, out uint op_bits)) - { - return BitConverter.Int32BitsToSingle((int)op_bits); - } - - // Return the most positive zero - if ((op1_bits & op2_bits) == 0x80000000u) - { - return BitConverter.Int32BitsToSingle(int.MinValue); - } - - return 0; - } - - public static double Max(double op1, double op2) - { - // Fast path - if (op1 > op2) - { - return op1; - } - - if (op1 < op2 || (op1 == op2 && op2 != 0)) - { - return op2; - } - - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); - - // Handle NaN cases - if (ProcessNaNs(op1_bits, op2_bits, out ulong op_bits)) - { - return BitConverter.Int64BitsToDouble((long)op_bits); - } - - // Return the most positive zero - if ((op1_bits & op2_bits) == 0x8000000000000000ul) - { - return BitConverter.Int64BitsToDouble(long.MinValue); - } - - return 0; - } - - public static float MinNum(float op1, float op2) - { - uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); - uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); - - if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) - { - op1 = float.PositiveInfinity; - } - else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) - { - op2 = float.PositiveInfinity; - } - - return Min(op1, op2); - } - - public static double MinNum(double op1, double op2) - { - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); - - if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) - { - op1 = double.PositiveInfinity; - } - else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) - { - op2 = double.PositiveInfinity; - } - - return Min(op1, op2); - } - - public static float Min(float op1, float op2) - { - // Fast path - if (op1 < op2) - { - return op1; - } - - if (op1 > op2 || (op1 == op2 && op2 != 0)) - { - return op2; - } - - uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); - uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); - - // Handle NaN cases - if (ProcessNaNs(op1_bits, op2_bits, out uint op_bits)) - { - return BitConverter.Int32BitsToSingle((int)op_bits); - } - - // Return the most negative zero - if ((op1_bits | op2_bits) == 0x80000000u) - { - return BitConverter.Int32BitsToSingle(int.MinValue); - } - - return 0; - } - - public static double Min(double op1, double op2) - { - // Fast path - if (op1 < op2) - { - return op1; - } - - if (op1 > op2 || (op1 == op2 && op2 != 0)) - { - return op2; - } - - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); - - // Handle NaN cases - if (ProcessNaNs(op1_bits, op2_bits, out ulong op_bits)) - { - return BitConverter.Int64BitsToDouble((long)op_bits); - } - - // Return the most negative zero - if ((op1_bits | op2_bits) == 0x8000000000000000ul) - { - return BitConverter.Int64BitsToDouble(long.MinValue); - } - - return 0; - } - - private static bool ProcessNaNs(uint op1_bits, uint op2_bits, out uint op_bits) - { - if (IsSNaN(op1_bits)) - { - op_bits = op1_bits | (1u << 22); // op1 is SNaN, return QNaN op1 - } - else if (IsSNaN(op2_bits)) - { - op_bits = op2_bits | (1u << 22); // op2 is SNaN, return QNaN op2 - } - else if (IsQNaN(op1_bits)) - { - op_bits = op1_bits; // op1 is QNaN, return QNaN op1 - } - else if (IsQNaN(op2_bits)) - { - op_bits = op2_bits; // op2 is QNaN, return QNaN op2 - } - else - { - op_bits = 0; - - return false; - } - - return true; - } - - private static bool ProcessNaNs(ulong op1_bits, ulong op2_bits, out ulong op_bits) - { - if (IsSNaN(op1_bits)) - { - op_bits = op1_bits | (1ul << 51); // op1 is SNaN, return QNaN op1 - } - else if (IsSNaN(op2_bits)) - { - op_bits = op2_bits | (1ul << 51); // op2 is SNaN, return QNaN op2 - } - else if (IsQNaN(op1_bits)) - { - op_bits = op1_bits; // op1 is QNaN, return QNaN op1 - } - else if (IsQNaN(op2_bits)) - { - op_bits = op2_bits; // op2 is QNaN, return QNaN op2 - } - else - { - op_bits = 0; - - return false; - } - - return true; - } - - private static bool IsQNaN(uint op_bits) - { - return (op_bits & 0x007FFFFF) != 0 && - (op_bits & 0x7FC00000) == 0x7FC00000; - } - - private static bool IsQNaN(ulong op_bits) - { - return (op_bits & 0x000FFFFFFFFFFFFF) != 0 && - (op_bits & 0x7FF8000000000000) == 0x7FF8000000000000; - } - - private static bool IsSNaN(uint op_bits) - { - return (op_bits & 0x007FFFFF) != 0 && - (op_bits & 0x7FC00000) == 0x7F800000; - } - - private static bool IsSNaN(ulong op_bits) - { - return (op_bits & 0x000FFFFFFFFFFFFF) != 0 && - (op_bits & 0x7FF8000000000000) == 0x7FF0000000000000; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AVectorHelper.cs b/ChocolArm64/Instruction/AVectorHelper.cs deleted file mode 100644 index 3e4452abbc..0000000000 --- a/ChocolArm64/Instruction/AVectorHelper.cs +++ /dev/null @@ -1,584 +0,0 @@ -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace ChocolArm64.Instruction -{ - static class AVectorHelper - { - private static readonly Vector128 Zero32_128Mask; - - static AVectorHelper() - { - if (!Sse2.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - Zero32_128Mask = Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0xffffffff)); - } - - public static void EmitCall(AILEmitterCtx Context, string Name64, string Name128) - { - bool IsSimd64 = Context.CurrOp.RegisterSize == ARegisterSize.SIMD64; - - Context.EmitCall(typeof(AVectorHelper), IsSimd64 ? Name64 : Name128); - } - - public static void EmitCall(AILEmitterCtx Context, string MthdName) - { - Context.EmitCall(typeof(AVectorHelper), MthdName); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SatF32ToS32(float Value) - { - if (float.IsNaN(Value)) return 0; - - return Value > int.MaxValue ? int.MaxValue : - Value < int.MinValue ? int.MinValue : (int)Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long SatF32ToS64(float Value) - { - if (float.IsNaN(Value)) return 0; - - return Value > long.MaxValue ? long.MaxValue : - Value < long.MinValue ? long.MinValue : (long)Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint SatF32ToU32(float Value) - { - if (float.IsNaN(Value)) return 0; - - return Value > uint.MaxValue ? uint.MaxValue : - Value < uint.MinValue ? uint.MinValue : (uint)Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong SatF32ToU64(float Value) - { - if (float.IsNaN(Value)) return 0; - - return Value > ulong.MaxValue ? ulong.MaxValue : - Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SatF64ToS32(double Value) - { - if (double.IsNaN(Value)) return 0; - - return Value > int.MaxValue ? int.MaxValue : - Value < int.MinValue ? int.MinValue : (int)Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long SatF64ToS64(double Value) - { - if (double.IsNaN(Value)) return 0; - - return Value > long.MaxValue ? long.MaxValue : - Value < long.MinValue ? long.MinValue : (long)Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint SatF64ToU32(double Value) - { - if (double.IsNaN(Value)) return 0; - - return Value > uint.MaxValue ? uint.MaxValue : - Value < uint.MinValue ? uint.MinValue : (uint)Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong SatF64ToU64(double Value) - { - if (double.IsNaN(Value)) return 0; - - return Value > ulong.MaxValue ? ulong.MaxValue : - Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; - } - - public static double Round(double Value, int Fpcr) - { - switch ((ARoundMode)((Fpcr >> 22) & 3)) - { - case ARoundMode.ToNearest: return Math.Round (Value); - case ARoundMode.TowardsPlusInfinity: return Math.Ceiling (Value); - case ARoundMode.TowardsMinusInfinity: return Math.Floor (Value); - case ARoundMode.TowardsZero: return Math.Truncate(Value); - } - - throw new InvalidOperationException(); - } - - public static float RoundF(float Value, int Fpcr) - { - switch ((ARoundMode)((Fpcr >> 22) & 3)) - { - case ARoundMode.ToNearest: return MathF.Round (Value); - case ARoundMode.TowardsPlusInfinity: return MathF.Ceiling (Value); - case ARoundMode.TowardsMinusInfinity: return MathF.Floor (Value); - case ARoundMode.TowardsZero: return MathF.Truncate(Value); - } - - throw new InvalidOperationException(); - } - - public static Vector128 Tbl1_V64( - Vector128 Vector, - Vector128 Tb0) - { - return Tbl(Vector, 8, Tb0); - } - - public static Vector128 Tbl1_V128( - Vector128 Vector, - Vector128 Tb0) - { - return Tbl(Vector, 16, Tb0); - } - - public static Vector128 Tbl2_V64( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1) - { - return Tbl(Vector, 8, Tb0, Tb1); - } - - public static Vector128 Tbl2_V128( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1) - { - return Tbl(Vector, 16, Tb0, Tb1); - } - - public static Vector128 Tbl3_V64( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1, - Vector128 Tb2) - { - return Tbl(Vector, 8, Tb0, Tb1, Tb2); - } - - public static Vector128 Tbl3_V128( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1, - Vector128 Tb2) - { - return Tbl(Vector, 16, Tb0, Tb1, Tb2); - } - - public static Vector128 Tbl4_V64( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1, - Vector128 Tb2, - Vector128 Tb3) - { - return Tbl(Vector, 8, Tb0, Tb1, Tb2, Tb3); - } - - public static Vector128 Tbl4_V128( - Vector128 Vector, - Vector128 Tb0, - Vector128 Tb1, - Vector128 Tb2, - Vector128 Tb3) - { - return Tbl(Vector, 16, Tb0, Tb1, Tb2, Tb3); - } - - private static Vector128 Tbl(Vector128 Vector, int Bytes, params Vector128[] Tb) - { - Vector128 Res = new Vector128(); - - byte[] Table = new byte[Tb.Length * 16]; - - for (byte Index = 0; Index < Tb.Length; Index++) - for (byte Index2 = 0; Index2 < 16; Index2++) - { - Table[Index * 16 + Index2] = (byte)VectorExtractIntZx(Tb[Index], Index2, 0); - } - - for (byte Index = 0; Index < Bytes; Index++) - { - byte TblIdx = (byte)VectorExtractIntZx(Vector, Index, 0); - - if (TblIdx < Table.Length) - { - Res = VectorInsertInt(Table[TblIdx], Res, Index, 0); - } - } - - return Res; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double VectorExtractDouble(Vector128 Vector, byte Index) - { - return BitConverter.Int64BitsToDouble(VectorExtractIntSx(Vector, Index, 3)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long VectorExtractIntSx(Vector128 Vector, byte Index, int Size) - { - if (Sse41.IsSupported) - { - switch (Size) - { - case 0: - return (sbyte)Sse41.Extract(Sse.StaticCast(Vector), Index); - - case 1: - return (short)Sse2.Extract(Sse.StaticCast(Vector), Index); - - case 2: - return Sse41.Extract(Sse.StaticCast(Vector), Index); - - case 3: - return Sse41.Extract(Sse.StaticCast(Vector), Index); - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } - else if (Sse2.IsSupported) - { - switch (Size) - { - case 0: - return (sbyte)VectorExtractIntZx(Vector, Index, Size); - - case 1: - return (short)VectorExtractIntZx(Vector, Index, Size); - - case 2: - return (int)VectorExtractIntZx(Vector, Index, Size); - - case 3: - return (long)VectorExtractIntZx(Vector, Index, Size); - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong VectorExtractIntZx(Vector128 Vector, byte Index, int Size) - { - if (Sse41.IsSupported) - { - switch (Size) - { - case 0: - return Sse41.Extract(Sse.StaticCast(Vector), Index); - - case 1: - return Sse2.Extract(Sse.StaticCast(Vector), Index); - - case 2: - return Sse41.Extract(Sse.StaticCast(Vector), Index); - - case 3: - return Sse41.Extract(Sse.StaticCast(Vector), Index); - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } - else if (Sse2.IsSupported) - { - int ShortIdx = Size == 0 - ? Index >> 1 - : Index << (Size - 1); - - ushort Value = Sse2.Extract(Sse.StaticCast(Vector), (byte)ShortIdx); - - switch (Size) - { - case 0: - return (byte)(Value >> (Index & 1) * 8); - - case 1: - return Value; - - case 2: - case 3: - { - ushort Value1 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 1)); - - if (Size == 2) - { - return (uint)(Value | (Value1 << 16)); - } - - ushort Value2 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 2)); - ushort Value3 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 3)); - - return ((ulong)Value << 0) | - ((ulong)Value1 << 16) | - ((ulong)Value2 << 32) | - ((ulong)Value3 << 48); - } - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float VectorExtractSingle(Vector128 Vector, byte Index) - { - if (Sse41.IsSupported) - { - return Sse41.Extract(Vector, Index); - } - else if (Sse2.IsSupported) - { - Vector128 ShortVector = Sse.StaticCast(Vector); - - int Low = Sse2.Extract(ShortVector, (byte)(Index * 2 + 0)); - int High = Sse2.Extract(ShortVector, (byte)(Index * 2 + 1)); - - return BitConverter.Int32BitsToSingle(Low | (High << 16)); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInsertDouble(double Value, Vector128 Vector, byte Index) - { - return VectorInsertInt((ulong)BitConverter.DoubleToInt64Bits(Value), Vector, Index, 3); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInsertInt(ulong Value, Vector128 Vector, byte Index, int Size) - { - if (Sse41.IsSupported) - { - switch (Size) - { - case 0: - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), (byte)Value, Index)); - - case 1: - return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(Vector), (ushort)Value, Index)); - - case 2: - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), (uint)Value, Index)); - - case 3: - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), Value, Index)); - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } - else if (Sse2.IsSupported) - { - Vector128 ShortVector = Sse.StaticCast(Vector); - - int ShortIdx = Size == 0 - ? Index >> 1 - : Index << (Size - 1); - - switch (Size) - { - case 0: - { - ushort ShortVal = Sse2.Extract(Sse.StaticCast(Vector), (byte)ShortIdx); - - int Shift = (Index & 1) * 8; - - ShortVal &= (ushort)(0xff00 >> Shift); - - ShortVal |= (ushort)((byte)Value << Shift); - - return Sse.StaticCast(Sse2.Insert(ShortVector, ShortVal, (byte)ShortIdx)); - } - - case 1: - return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(Vector), (ushort)Value, Index)); - - case 2: - case 3: - { - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 0), (byte)(ShortIdx + 0)); - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 16), (byte)(ShortIdx + 1)); - - if (Size == 3) - { - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 32), (byte)(ShortIdx + 2)); - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 48), (byte)(ShortIdx + 3)); - } - - return Sse.StaticCast(ShortVector); - } - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInsertSingle(float Value, Vector128 Vector, byte Index) - { - if (Sse41.IsSupported) - { - return Sse41.Insert(Vector, Value, (byte)(Index << 4)); - } - else if (Sse2.IsSupported) - { - int IntValue = BitConverter.SingleToInt32Bits(Value); - - ushort Low = (ushort)(IntValue >> 0); - ushort High = (ushort)(IntValue >> 16); - - Vector128 ShortVector = Sse.StaticCast(Vector); - - ShortVector = Sse2.Insert(ShortVector, Low, (byte)(Index * 2 + 0)); - ShortVector = Sse2.Insert(ShortVector, High, (byte)(Index * 2 + 1)); - - return Sse.StaticCast(ShortVector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorZero32_128(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.And(Vector, Zero32_128Mask); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToSByte(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt16(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt32(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt64(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToDouble(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSByteToSingle(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt16ToSingle(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt32ToSingle(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt64ToSingle(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorDoubleToSingle(Vector128 Vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(Vector); - } - - throw new PlatformNotSupportedException(); - } - } -} diff --git a/ChocolArm64/Instruction32/A32InstInterpretFlow.cs b/ChocolArm64/Instruction32/A32InstInterpretFlow.cs deleted file mode 100644 index 223fd186ca..0000000000 --- a/ChocolArm64/Instruction32/A32InstInterpretFlow.cs +++ /dev/null @@ -1,70 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Decoder32; -using ChocolArm64.Memory; -using ChocolArm64.State; - -using static ChocolArm64.Instruction32.A32InstInterpretHelper; - -namespace ChocolArm64.Instruction32 -{ - static partial class A32InstInterpret - { - public static void B(AThreadState State, AMemory Memory, AOpCode OpCode) - { - A32OpCodeBImmAl Op = (A32OpCodeBImmAl)OpCode; - - if (IsConditionTrue(State, Op.Cond)) - { - BranchWritePc(State, GetPc(State) + (uint)Op.Imm); - } - } - - public static void Bl(AThreadState State, AMemory Memory, AOpCode OpCode) - { - Blx(State, Memory, OpCode, false); - } - - public static void Blx(AThreadState State, AMemory Memory, AOpCode OpCode) - { - Blx(State, Memory, OpCode, true); - } - - public static void Blx(AThreadState State, AMemory Memory, AOpCode OpCode, bool X) - { - A32OpCodeBImmAl Op = (A32OpCodeBImmAl)OpCode; - - if (IsConditionTrue(State, Op.Cond)) - { - uint Pc = GetPc(State); - - if (State.Thumb) - { - State.R14 = Pc | 1; - } - else - { - State.R14 = Pc - 4U; - } - - if (X) - { - State.Thumb = !State.Thumb; - } - - if (!State.Thumb) - { - Pc &= ~3U; - } - - BranchWritePc(State, Pc + (uint)Op.Imm); - } - } - - private static void BranchWritePc(AThreadState State, uint Pc) - { - State.R15 = State.Thumb - ? Pc & ~1U - : Pc & ~3U; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction32/A32InstInterpretHelper.cs b/ChocolArm64/Instruction32/A32InstInterpretHelper.cs deleted file mode 100644 index 9c3c098e8e..0000000000 --- a/ChocolArm64/Instruction32/A32InstInterpretHelper.cs +++ /dev/null @@ -1,65 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using System; - -namespace ChocolArm64.Instruction32 -{ - static class A32InstInterpretHelper - { - public static bool IsConditionTrue(AThreadState State, ACond Cond) - { - switch (Cond) - { - case ACond.Eq: return State.Zero; - case ACond.Ne: return !State.Zero; - case ACond.Ge_Un: return State.Carry; - case ACond.Lt_Un: return !State.Carry; - case ACond.Mi: return State.Negative; - case ACond.Pl: return !State.Negative; - case ACond.Vs: return State.Overflow; - case ACond.Vc: return !State.Overflow; - case ACond.Gt_Un: return State.Carry && !State.Zero; - case ACond.Le_Un: return !State.Carry && State.Zero; - case ACond.Ge: return State.Negative == State.Overflow; - case ACond.Lt: return State.Negative != State.Overflow; - case ACond.Gt: return State.Negative == State.Overflow && !State.Zero; - case ACond.Le: return State.Negative != State.Overflow && State.Zero; - } - - return true; - } - - public unsafe static uint GetReg(AThreadState State, int Reg) - { - if ((uint)Reg > 15) - { - throw new ArgumentOutOfRangeException(nameof(Reg)); - } - - fixed (uint* Ptr = &State.R0) - { - return *(Ptr + Reg); - } - } - - public unsafe static void SetReg(AThreadState State, int Reg, uint Value) - { - if ((uint)Reg > 15) - { - throw new ArgumentOutOfRangeException(nameof(Reg)); - } - - fixed (uint* Ptr = &State.R0) - { - *(Ptr + Reg) = Value; - } - } - - public static uint GetPc(AThreadState State) - { - //Due to the old fetch-decode-execute pipeline of old ARM CPUs, - //the PC is 4 or 8 bytes (2 instructions) ahead of the current instruction. - return State.R15 + (State.Thumb ? 2U : 4U); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instruction/ACryptoHelper.cs b/ChocolArm64/Instructions/CryptoHelper.cs similarity index 75% rename from ChocolArm64/Instruction/ACryptoHelper.cs rename to ChocolArm64/Instructions/CryptoHelper.cs index 2dc65972b2..b38d79a8c7 100644 --- a/ChocolArm64/Instruction/ACryptoHelper.cs +++ b/ChocolArm64/Instructions/CryptoHelper.cs @@ -4,12 +4,12 @@ using System; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; -namespace ChocolArm64.Instruction +namespace ChocolArm64.Instructions { - static class ACryptoHelper + static class CryptoHelper { #region "LookUp Tables" - private static byte[] SBox = + private static byte[] _sBox = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, @@ -29,7 +29,7 @@ namespace ChocolArm64.Instruction 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; - private static byte[] InvSBox = + private static byte[] _invSBox = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, @@ -49,7 +49,7 @@ namespace ChocolArm64.Instruction 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; - private static byte[] GFMul_02 = + private static byte[] _gfMul02 = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, @@ -69,7 +69,7 @@ namespace ChocolArm64.Instruction 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 }; - private static byte[] GFMul_03 = + private static byte[] _gfMul03 = { 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, @@ -89,7 +89,7 @@ namespace ChocolArm64.Instruction 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a }; - private static byte[] GFMul_09 = + private static byte[] _gfMul09 = { 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, @@ -109,7 +109,7 @@ namespace ChocolArm64.Instruction 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 }; - private static byte[] GFMul_0B = + private static byte[] _gfMul0B = { 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, @@ -129,7 +129,7 @@ namespace ChocolArm64.Instruction 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 }; - private static byte[] GFMul_0D = + private static byte[] _gfMul0D = { 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, @@ -149,7 +149,7 @@ namespace ChocolArm64.Instruction 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 }; - private static byte[] GFMul_0E = + private static byte[] _gfMul0E = { 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, @@ -169,149 +169,149 @@ namespace ChocolArm64.Instruction 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d }; - private static byte[] SRPerm = { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 }; + private static byte[] _srPerm = { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 }; - private static byte[] ISRPerm = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 }; + private static byte[] _isrPerm = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 }; #endregion - public static Vector128 AESInvMixColumns(Vector128 op) + public static Vector128 AesInvMixColumns(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Columns = 0; Columns <= 3; Columns++) + for (int columns = 0; columns <= 3; columns++) { - int Idx = Columns << 2; + int idx = columns << 2; - byte Row0 = InState[Idx + 0]; // A, E, I, M: [Row0, Col0-Col3] - byte Row1 = InState[Idx + 1]; // B, F, J, N: [Row1, Col0-Col3] - byte Row2 = InState[Idx + 2]; // C, G, K, O: [Row2, Col0-Col3] - byte Row3 = InState[Idx + 3]; // D, H, L, P: [Row3, Col0-Col3] + byte row0 = inState[idx + 0]; // A, E, I, M: [row0, col0-col3] + byte row1 = inState[idx + 1]; // B, F, J, N: [row1, col0-col3] + byte row2 = inState[idx + 2]; // C, G, K, O: [row2, col0-col3] + byte row3 = inState[idx + 3]; // D, H, L, P: [row3, col0-col3] - OutState[Idx + 0] = (byte)((uint)GFMul_0E[Row0] ^ GFMul_0B[Row1] ^ GFMul_0D[Row2] ^ GFMul_09[Row3]); - OutState[Idx + 1] = (byte)((uint)GFMul_09[Row0] ^ GFMul_0E[Row1] ^ GFMul_0B[Row2] ^ GFMul_0D[Row3]); - OutState[Idx + 2] = (byte)((uint)GFMul_0D[Row0] ^ GFMul_09[Row1] ^ GFMul_0E[Row2] ^ GFMul_0B[Row3]); - OutState[Idx + 3] = (byte)((uint)GFMul_0B[Row0] ^ GFMul_0D[Row1] ^ GFMul_09[Row2] ^ GFMul_0E[Row3]); + outState[idx + 0] = (byte)((uint)_gfMul0E[row0] ^ _gfMul0B[row1] ^ _gfMul0D[row2] ^ _gfMul09[row3]); + outState[idx + 1] = (byte)((uint)_gfMul09[row0] ^ _gfMul0E[row1] ^ _gfMul0B[row2] ^ _gfMul0D[row3]); + outState[idx + 2] = (byte)((uint)_gfMul0D[row0] ^ _gfMul09[row1] ^ _gfMul0E[row2] ^ _gfMul0B[row3]); + outState[idx + 3] = (byte)((uint)_gfMul0B[row0] ^ _gfMul0D[row1] ^ _gfMul09[row2] ^ _gfMul0E[row3]); } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESInvShiftRows(Vector128 op) + public static Vector128 AesInvShiftRows(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Idx = 0; Idx <= 15; Idx++) + for (int idx = 0; idx <= 15; idx++) { - OutState[ISRPerm[Idx]] = InState[Idx]; + outState[_isrPerm[idx]] = inState[idx]; } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESInvSubBytes(Vector128 op) + public static Vector128 AesInvSubBytes(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Idx = 0; Idx <= 15; Idx++) + for (int idx = 0; idx <= 15; idx++) { - OutState[Idx] = InvSBox[InState[Idx]]; + outState[idx] = _invSBox[inState[idx]]; } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESMixColumns(Vector128 op) + public static Vector128 AesMixColumns(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Columns = 0; Columns <= 3; Columns++) + for (int columns = 0; columns <= 3; columns++) { - int Idx = Columns << 2; + int idx = columns << 2; - byte Row0 = InState[Idx + 0]; // A, E, I, M: [Row0, Col0-Col3] - byte Row1 = InState[Idx + 1]; // B, F, J, N: [Row1, Col0-Col3] - byte Row2 = InState[Idx + 2]; // C, G, K, O: [Row2, Col0-Col3] - byte Row3 = InState[Idx + 3]; // D, H, L, P: [Row3, Col0-Col3] + byte row0 = inState[idx + 0]; // A, E, I, M: [row0, col0-col3] + byte row1 = inState[idx + 1]; // B, F, J, N: [row1, col0-col3] + byte row2 = inState[idx + 2]; // C, G, K, O: [row2, col0-col3] + byte row3 = inState[idx + 3]; // D, H, L, P: [row3, col0-col3] - OutState[Idx + 0] = (byte)((uint)GFMul_02[Row0] ^ GFMul_03[Row1] ^ Row2 ^ Row3); - OutState[Idx + 1] = (byte)((uint)Row0 ^ GFMul_02[Row1] ^ GFMul_03[Row2] ^ Row3); - OutState[Idx + 2] = (byte)((uint)Row0 ^ Row1 ^ GFMul_02[Row2] ^ GFMul_03[Row3]); - OutState[Idx + 3] = (byte)((uint)GFMul_03[Row0] ^ Row1 ^ Row2 ^ GFMul_02[Row3]); + outState[idx + 0] = (byte)((uint)_gfMul02[row0] ^ _gfMul03[row1] ^ row2 ^ row3); + outState[idx + 1] = (byte)((uint)row0 ^ _gfMul02[row1] ^ _gfMul03[row2] ^ row3); + outState[idx + 2] = (byte)((uint)row0 ^ row1 ^ _gfMul02[row2] ^ _gfMul03[row3]); + outState[idx + 3] = (byte)((uint)_gfMul03[row0] ^ row1 ^ row2 ^ _gfMul02[row3]); } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESShiftRows(Vector128 op) + public static Vector128 AesShiftRows(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Idx = 0; Idx <= 15; Idx++) + for (int idx = 0; idx <= 15; idx++) { - OutState[SRPerm[Idx]] = InState[Idx]; + outState[_srPerm[idx]] = inState[idx]; } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - public static Vector128 AESSubBytes(Vector128 op) + public static Vector128 AesSubBytes(Vector128 op) { - byte[] InState = new byte[16]; - byte[] OutState = new byte[16]; + byte[] inState = new byte[16]; + byte[] outState = new byte[16]; - FromVectorToByteArray(InState, ref op); + FromVectorToByteArray(inState, ref op); - for (int Idx = 0; Idx <= 15; Idx++) + for (int idx = 0; idx <= 15; idx++) { - OutState[Idx] = SBox[InState[Idx]]; + outState[idx] = _sBox[inState[idx]]; } - FromByteArrayToVector(OutState, ref op); + FromByteArrayToVector(outState, ref op); return op; } - private static void FromVectorToByteArray(byte[] State, ref Vector128 op) + private static void FromVectorToByteArray(byte[] state, ref Vector128 op) { - ulong ULongLow = AVectorHelper.VectorExtractIntZx((op), (byte)0, 3); - ulong ULongHigh = AVectorHelper.VectorExtractIntZx((op), (byte)1, 3); + ulong uLongLow = VectorHelper.VectorExtractIntZx((op), (byte)0, 3); + ulong uLongHigh = VectorHelper.VectorExtractIntZx((op), (byte)1, 3); - for (int Idx = 0; Idx <= 7; Idx++) + for (int idx = 0; idx <= 7; idx++) { - State[Idx + 0] = (byte)(ULongLow & 0xFFUL); - State[Idx + 8] = (byte)(ULongHigh & 0xFFUL); + state[idx + 0] = (byte)(uLongLow & 0xFFUL); + state[idx + 8] = (byte)(uLongHigh & 0xFFUL); - ULongLow >>= 8; - ULongHigh >>= 8; + uLongLow >>= 8; + uLongHigh >>= 8; } } - private static void FromByteArrayToVector(byte[] State, ref Vector128 op) + private static void FromByteArrayToVector(byte[] state, ref Vector128 op) { if (!Sse2.IsSupported) { @@ -319,10 +319,10 @@ namespace ChocolArm64.Instruction } op = Sse.StaticCast(Sse2.SetVector128( - State[15], State[14], State[13], State[12], - State[11], State[10], State[9], State[8], - State[7], State[6], State[5], State[4], - State[3], State[2], State[1], State[0])); + state[15], state[14], state[13], state[12], + state[11], state[10], state[9], state[8], + state[7], state[6], state[5], state[4], + state[3], state[2], state[1], state[0])); } } } diff --git a/ChocolArm64/Instructions/Inst.cs b/ChocolArm64/Instructions/Inst.cs new file mode 100644 index 0000000000..5f6740caec --- /dev/null +++ b/ChocolArm64/Instructions/Inst.cs @@ -0,0 +1,20 @@ +using System; + +namespace ChocolArm64.Instructions +{ + struct Inst + { + public InstInterpreter Interpreter { get; private set; } + public InstEmitter Emitter { get; private set; } + public Type Type { get; private set; } + + public static Inst Undefined => new Inst(null, InstEmit.Und, null); + + public Inst(InstInterpreter interpreter, InstEmitter emitter, Type type) + { + Interpreter = interpreter; + Emitter = emitter; + Type = type; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitAlu.cs b/ChocolArm64/Instructions/InstEmitAlu.cs new file mode 100644 index 0000000000..c0258ed2b7 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitAlu.cs @@ -0,0 +1,402 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitAluHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Adc(ILEmitterCtx context) => EmitAdc(context, false); + public static void Adcs(ILEmitterCtx context) => EmitAdc(context, true); + + private static void EmitAdc(ILEmitterCtx context, bool setFlags) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Add); + + context.EmitLdflg((int)PState.CBit); + + Type[] mthdTypes = new Type[] { typeof(bool) }; + + MethodInfo mthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), mthdTypes); + + context.EmitCall(mthdInfo); + + if (context.CurrOp.RegisterSize != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.Emit(OpCodes.Add); + + if (setFlags) + { + context.EmitZnFlagCheck(); + + EmitAdcsCCheck(context); + EmitAddsVCheck(context); + } + + EmitDataStore(context); + } + + public static void Add(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Add); + + public static void Adds(ILEmitterCtx context) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Add); + + context.EmitZnFlagCheck(); + + EmitAddsCCheck(context); + EmitAddsVCheck(context); + EmitDataStoreS(context); + } + + public static void And(ILEmitterCtx context) => EmitDataOp(context, OpCodes.And); + + public static void Ands(ILEmitterCtx context) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.And); + + EmitZeroCvFlags(context); + + context.EmitZnFlagCheck(); + + EmitDataStoreS(context); + } + + public static void Asrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr); + + public static void Bic(ILEmitterCtx context) => EmitBic(context, false); + public static void Bics(ILEmitterCtx context) => EmitBic(context, true); + + private static void EmitBic(ILEmitterCtx context, bool setFlags) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Not); + context.Emit(OpCodes.And); + + if (setFlags) + { + EmitZeroCvFlags(context); + + context.EmitZnFlagCheck(); + } + + EmitDataStore(context, setFlags); + } + + public static void Cls(ILEmitterCtx context) + { + OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + context.EmitLdc_I4(op.RegisterSize == RegisterSize.Int32 ? 32 : 64); + + SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingSigns)); + + context.EmitStintzr(op.Rd); + } + + public static void Clz(ILEmitterCtx context) + { + OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (Lzcnt.IsSupported) + { + Type tValue = op.RegisterSize == RegisterSize.Int32 ? typeof(uint) : typeof(ulong); + + context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { tValue })); + } + else + { + context.EmitLdc_I4(op.RegisterSize == RegisterSize.Int32 ? 32 : 64); + + SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingZeros)); + } + + context.EmitStintzr(op.Rd); + } + + public static void Eon(ILEmitterCtx context) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Not); + context.Emit(OpCodes.Xor); + + EmitDataStore(context); + } + + public static void Eor(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Xor); + + public static void Extr(ILEmitterCtx context) + { + //TODO: Ensure that the Shift is valid for the Is64Bits. + OpCodeAluRs64 op = (OpCodeAluRs64)context.CurrOp; + + context.EmitLdintzr(op.Rm); + + if (op.Shift > 0) + { + context.EmitLdc_I4(op.Shift); + + context.Emit(OpCodes.Shr_Un); + + context.EmitLdintzr(op.Rn); + context.EmitLdc_I4(op.GetBitsCount() - op.Shift); + + context.Emit(OpCodes.Shl); + context.Emit(OpCodes.Or); + } + + EmitDataStore(context); + } + + public static void Lslv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shl); + public static void Lsrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr_Un); + + public static void Sbc(ILEmitterCtx context) => EmitSbc(context, false); + public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true); + + private static void EmitSbc(ILEmitterCtx context, bool setFlags) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Sub); + + context.EmitLdflg((int)PState.CBit); + + Type[] mthdTypes = new Type[] { typeof(bool) }; + + MethodInfo mthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), mthdTypes); + + context.EmitCall(mthdInfo); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.Xor); + + if (context.CurrOp.RegisterSize != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.Emit(OpCodes.Sub); + + if (setFlags) + { + context.EmitZnFlagCheck(); + + EmitSbcsCCheck(context); + EmitSubsVCheck(context); + } + + EmitDataStore(context); + } + + public static void Sub(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Sub); + + public static void Subs(ILEmitterCtx context) + { + context.TryOptMarkCondWithoutCmp(); + + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Sub); + + context.EmitZnFlagCheck(); + + EmitSubsCCheck(context); + EmitSubsVCheck(context); + EmitDataStoreS(context); + } + + public static void Orn(ILEmitterCtx context) + { + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Not); + context.Emit(OpCodes.Or); + + EmitDataStore(context); + } + + public static void Orr(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Or); + + public static void Rbit(ILEmitterCtx context) => EmitFallback32_64(context, + nameof(SoftFallback.ReverseBits32), + nameof(SoftFallback.ReverseBits64)); + + public static void Rev16(ILEmitterCtx context) => EmitFallback32_64(context, + nameof(SoftFallback.ReverseBytes16_32), + nameof(SoftFallback.ReverseBytes16_64)); + + public static void Rev32(ILEmitterCtx context) => EmitFallback32_64(context, + nameof(SoftFallback.ReverseBytes32_32), + nameof(SoftFallback.ReverseBytes32_64)); + + private static void EmitFallback32_64(ILEmitterCtx context, string name32, string name64) + { + OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (op.RegisterSize == RegisterSize.Int32) + { + SoftFallback.EmitCall(context, name32); + } + else + { + SoftFallback.EmitCall(context, name64); + } + + context.EmitStintzr(op.Rd); + } + + public static void Rev64(ILEmitterCtx context) + { + OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.ReverseBytes64)); + + context.EmitStintzr(op.Rd); + } + + public static void Rorv(ILEmitterCtx context) + { + EmitDataLoadRn(context); + EmitDataLoadShift(context); + + context.Emit(OpCodes.Shr_Un); + + EmitDataLoadRn(context); + + context.EmitLdc_I4(context.CurrOp.GetBitsCount()); + + EmitDataLoadShift(context); + + context.Emit(OpCodes.Sub); + context.Emit(OpCodes.Shl); + context.Emit(OpCodes.Or); + + EmitDataStore(context); + } + + public static void Sdiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div); + public static void Udiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div_Un); + + private static void EmitDiv(ILEmitterCtx context, OpCode ilOp) + { + //If Rm == 0, Rd = 0 (division by zero). + context.EmitLdc_I(0); + + EmitDataLoadRm(context); + + context.EmitLdc_I(0); + + ILLabel badDiv = new ILLabel(); + + context.Emit(OpCodes.Beq_S, badDiv); + context.Emit(OpCodes.Pop); + + if (ilOp == OpCodes.Div) + { + //If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow). + long intMin = 1L << (context.CurrOp.GetBitsCount() - 1); + + context.EmitLdc_I(intMin); + + EmitDataLoadRn(context); + + context.EmitLdc_I(intMin); + + context.Emit(OpCodes.Ceq); + + EmitDataLoadRm(context); + + context.EmitLdc_I(-1); + + context.Emit(OpCodes.Ceq); + context.Emit(OpCodes.And); + context.Emit(OpCodes.Brtrue_S, badDiv); + context.Emit(OpCodes.Pop); + } + + EmitDataLoadRn(context); + EmitDataLoadRm(context); + + context.Emit(ilOp); + + context.MarkLabel(badDiv); + + EmitDataStore(context); + } + + private static void EmitDataOp(ILEmitterCtx context, OpCode ilOp) + { + EmitDataLoadOpers(context); + + context.Emit(ilOp); + + EmitDataStore(context); + } + + private static void EmitDataOpShift(ILEmitterCtx context, OpCode ilOp) + { + EmitDataLoadRn(context); + EmitDataLoadShift(context); + + context.Emit(ilOp); + + EmitDataStore(context); + } + + private static void EmitDataLoadShift(ILEmitterCtx context) + { + EmitDataLoadRm(context); + + context.EmitLdc_I(context.CurrOp.GetBitsCount() - 1); + + context.Emit(OpCodes.And); + + //Note: Only 32-bits shift values are valid, so when the value is 64-bits + //we need to cast it to a 32-bits integer. This is fine because we + //AND the value and only keep the lower 5 or 6 bits anyway -- it + //could very well fit on a byte. + if (context.CurrOp.RegisterSize != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_I4); + } + } + + private static void EmitZeroCvFlags(ILEmitterCtx context) + { + context.EmitLdc_I4(0); + + context.EmitStflg((int)PState.VBit); + + context.EmitLdc_I4(0); + + context.EmitStflg((int)PState.CBit); + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitAluHelper.cs b/ChocolArm64/Instructions/InstEmitAluHelper.cs new file mode 100644 index 0000000000..97c5056407 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitAluHelper.cs @@ -0,0 +1,221 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static class InstEmitAluHelper + { + public static void EmitAdcsCCheck(ILEmitterCtx context) + { + //C = (Rd == Rn && CIn) || Rd < Rn + context.EmitSttmp(); + context.EmitLdtmp(); + context.EmitLdtmp(); + + EmitDataLoadRn(context); + + context.Emit(OpCodes.Ceq); + + context.EmitLdflg((int)PState.CBit); + + context.Emit(OpCodes.And); + + context.EmitLdtmp(); + + EmitDataLoadRn(context); + + context.Emit(OpCodes.Clt_Un); + context.Emit(OpCodes.Or); + + context.EmitStflg((int)PState.CBit); + } + + public static void EmitAddsCCheck(ILEmitterCtx context) + { + //C = Rd < Rn + context.Emit(OpCodes.Dup); + + EmitDataLoadRn(context); + + context.Emit(OpCodes.Clt_Un); + + context.EmitStflg((int)PState.CBit); + } + + public static void EmitAddsVCheck(ILEmitterCtx context) + { + //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 + context.Emit(OpCodes.Dup); + + EmitDataLoadRn(context); + + context.Emit(OpCodes.Xor); + + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Xor); + context.Emit(OpCodes.Not); + context.Emit(OpCodes.And); + + context.EmitLdc_I(0); + + context.Emit(OpCodes.Clt); + + context.EmitStflg((int)PState.VBit); + } + + public static void EmitSbcsCCheck(ILEmitterCtx context) + { + //C = (Rn == Rm && CIn) || Rn > Rm + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Ceq); + + context.EmitLdflg((int)PState.CBit); + + context.Emit(OpCodes.And); + + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Cgt_Un); + context.Emit(OpCodes.Or); + + context.EmitStflg((int)PState.CBit); + } + + public static void EmitSubsCCheck(ILEmitterCtx context) + { + //C = Rn == Rm || Rn > Rm = !(Rn < Rm) + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Clt_Un); + + context.EmitLdc_I4(1); + + context.Emit(OpCodes.Xor); + + context.EmitStflg((int)PState.CBit); + } + + public static void EmitSubsVCheck(ILEmitterCtx context) + { + //V = (Rd ^ Rn) & (Rn ^ Rm) < 0 + context.Emit(OpCodes.Dup); + + EmitDataLoadRn(context); + + context.Emit(OpCodes.Xor); + + EmitDataLoadOpers(context); + + context.Emit(OpCodes.Xor); + context.Emit(OpCodes.And); + + context.EmitLdc_I(0); + + context.Emit(OpCodes.Clt); + + context.EmitStflg((int)PState.VBit); + } + + public static void EmitDataLoadRm(ILEmitterCtx context) + { + context.EmitLdintzr(((IOpCodeAluRs64)context.CurrOp).Rm); + } + + public static void EmitDataLoadOpers(ILEmitterCtx context) + { + EmitDataLoadRn(context); + EmitDataLoadOper2(context); + } + + public static void EmitDataLoadRn(ILEmitterCtx context) + { + IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; + + if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64) + { + context.EmitLdintzr(op.Rn); + } + else + { + context.EmitLdint(op.Rn); + } + } + + public static void EmitDataLoadOper2(ILEmitterCtx context) + { + switch (context.CurrOp) + { + case IOpCodeAluImm64 op: + context.EmitLdc_I(op.Imm); + break; + + case IOpCodeAluRs64 op: + context.EmitLdintzr(op.Rm); + + switch (op.ShiftType) + { + case ShiftType.Lsl: context.EmitLsl(op.Shift); break; + case ShiftType.Lsr: context.EmitLsr(op.Shift); break; + case ShiftType.Asr: context.EmitAsr(op.Shift); break; + case ShiftType.Ror: context.EmitRor(op.Shift); break; + } + break; + + case IOpCodeAluRx64 op: + context.EmitLdintzr(op.Rm); + context.EmitCast(op.IntType); + context.EmitLsl(op.Shift); + break; + } + } + + public static void EmitDataStore(ILEmitterCtx context) => EmitDataStore(context, false); + public static void EmitDataStoreS(ILEmitterCtx context) => EmitDataStore(context, true); + + public static void EmitDataStore(ILEmitterCtx context, bool setFlags) + { + IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp; + + if (setFlags || op is IOpCodeAluRs64) + { + context.EmitStintzr(op.Rd); + } + else + { + context.EmitStint(op.Rd); + } + } + + public static void EmitSetNzcv(ILEmitterCtx context) + { + context.Emit(OpCodes.Dup); + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.And); + context.EmitStflg((int)PState.VBit); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + context.Emit(OpCodes.Dup); + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.And); + context.EmitStflg((int)PState.CBit); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + context.Emit(OpCodes.Dup); + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.And); + context.EmitStflg((int)PState.ZBit); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.And); + context.EmitStflg((int)PState.NBit); + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitBfm.cs b/ChocolArm64/Instructions/InstEmitBfm.cs new file mode 100644 index 0000000000..d25af8be8a --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitBfm.cs @@ -0,0 +1,208 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Bfm(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + EmitBfmLoadRn(context); + + context.EmitLdintzr(op.Rd); + context.EmitLdc_I(~op.WMask & op.TMask); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Or); + + context.EmitLdintzr(op.Rd); + context.EmitLdc_I(~op.TMask); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Or); + + context.EmitStintzr(op.Rd); + } + + public static void Sbfm(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + int bitsCount = op.GetBitsCount(); + + if (op.Pos + 1 == bitsCount) + { + EmitSbfmShift(context); + } + else if (op.Pos < op.Shift) + { + EmitSbfiz(context); + } + else if (op.Pos == 7 && op.Shift == 0) + { + EmitSbfmCast(context, OpCodes.Conv_I1); + } + else if (op.Pos == 15 && op.Shift == 0) + { + EmitSbfmCast(context, OpCodes.Conv_I2); + } + else if (op.Pos == 31 && op.Shift == 0) + { + EmitSbfmCast(context, OpCodes.Conv_I4); + } + else + { + EmitBfmLoadRn(context); + + context.EmitLdintzr(op.Rn); + + context.EmitLsl(bitsCount - 1 - op.Pos); + context.EmitAsr(bitsCount - 1); + + context.EmitLdc_I(~op.TMask); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Or); + + context.EmitStintzr(op.Rd); + } + } + + public static void Ubfm(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + if (op.Pos + 1 == op.GetBitsCount()) + { + EmitUbfmShift(context); + } + else if (op.Pos < op.Shift) + { + EmitUbfiz(context); + } + else if (op.Pos + 1 == op.Shift) + { + EmitBfmLsl(context); + } + else if (op.Pos == 7 && op.Shift == 0) + { + EmitUbfmCast(context, OpCodes.Conv_U1); + } + else if (op.Pos == 15 && op.Shift == 0) + { + EmitUbfmCast(context, OpCodes.Conv_U2); + } + else + { + EmitBfmLoadRn(context); + + context.EmitStintzr(op.Rd); + } + } + + private static void EmitSbfiz(ILEmitterCtx context) => EmitBfiz(context, true); + private static void EmitUbfiz(ILEmitterCtx context) => EmitBfiz(context, false); + + private static void EmitBfiz(ILEmitterCtx context, bool signed) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + int width = op.Pos + 1; + + context.EmitLdintzr(op.Rn); + + context.EmitLsl(op.GetBitsCount() - width); + + if (signed) + { + context.EmitAsr(op.Shift - width); + } + else + { + context.EmitLsr(op.Shift - width); + } + + context.EmitStintzr(op.Rd); + } + + private static void EmitSbfmCast(ILEmitterCtx context, OpCode ilOp) + { + EmitBfmCast(context, ilOp, true); + } + + private static void EmitUbfmCast(ILEmitterCtx context, OpCode ilOp) + { + EmitBfmCast(context, ilOp, false); + } + + private static void EmitBfmCast(ILEmitterCtx context, OpCode ilOp, bool signed) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + context.Emit(ilOp); + + if (op.RegisterSize != RegisterSize.Int32) + { + context.Emit(signed + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rd); + } + + private static void EmitSbfmShift(ILEmitterCtx context) + { + EmitBfmShift(context, true); + } + + private static void EmitUbfmShift(ILEmitterCtx context) + { + EmitBfmShift(context, false); + } + + private static void EmitBfmShift(ILEmitterCtx context, bool signed) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + context.EmitLdc_I4(op.Shift); + + context.Emit(signed + ? OpCodes.Shr + : OpCodes.Shr_Un); + + context.EmitStintzr(op.Rd); + } + + private static void EmitBfmLsl(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + context.EmitLsl(op.GetBitsCount() - op.Shift); + + context.EmitStintzr(op.Rd); + } + + private static void EmitBfmLoadRn(ILEmitterCtx context) + { + OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + context.EmitRor(op.Shift); + + context.EmitLdc_I(op.WMask & op.TMask); + + context.Emit(OpCodes.And); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitCcmp.cs b/ChocolArm64/Instructions/InstEmitCcmp.cs new file mode 100644 index 0000000000..b91104c938 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitCcmp.cs @@ -0,0 +1,81 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmitAluHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + private enum CcmpOp + { + Cmp, + Cmn + } + + public static void Ccmn(ILEmitterCtx context) => EmitCcmp(context, CcmpOp.Cmn); + public static void Ccmp(ILEmitterCtx context) => EmitCcmp(context, CcmpOp.Cmp); + + private static void EmitCcmp(ILEmitterCtx context, CcmpOp cmpOp) + { + OpCodeCcmp64 op = (OpCodeCcmp64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.EmitCondBranch(lblTrue, op.Cond); + + context.EmitLdc_I4((op.Nzcv >> 0) & 1); + + context.EmitStflg((int)PState.VBit); + + context.EmitLdc_I4((op.Nzcv >> 1) & 1); + + context.EmitStflg((int)PState.CBit); + + context.EmitLdc_I4((op.Nzcv >> 2) & 1); + + context.EmitStflg((int)PState.ZBit); + + context.EmitLdc_I4((op.Nzcv >> 3) & 1); + + context.EmitStflg((int)PState.NBit); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblTrue); + + EmitDataLoadOpers(context); + + if (cmpOp == CcmpOp.Cmp) + { + context.Emit(OpCodes.Sub); + + context.EmitZnFlagCheck(); + + EmitSubsCCheck(context); + EmitSubsVCheck(context); + } + else if (cmpOp == CcmpOp.Cmn) + { + context.Emit(OpCodes.Add); + + context.EmitZnFlagCheck(); + + EmitAddsCCheck(context); + EmitAddsVCheck(context); + } + else + { + throw new ArgumentException(nameof(cmpOp)); + } + + context.Emit(OpCodes.Pop); + + context.MarkLabel(lblEnd); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitCsel.cs b/ChocolArm64/Instructions/InstEmitCsel.cs new file mode 100644 index 0000000000..19b073ceaf --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitCsel.cs @@ -0,0 +1,58 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + private enum CselOperation + { + None, + Increment, + Invert, + Negate + } + + public static void Csel(ILEmitterCtx context) => EmitCsel(context, CselOperation.None); + public static void Csinc(ILEmitterCtx context) => EmitCsel(context, CselOperation.Increment); + public static void Csinv(ILEmitterCtx context) => EmitCsel(context, CselOperation.Invert); + public static void Csneg(ILEmitterCtx context) => EmitCsel(context, CselOperation.Negate); + + private static void EmitCsel(ILEmitterCtx context, CselOperation cselOp) + { + OpCodeCsel64 op = (OpCodeCsel64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.EmitCondBranch(lblTrue, op.Cond); + context.EmitLdintzr(op.Rm); + + if (cselOp == CselOperation.Increment) + { + context.EmitLdc_I(1); + + context.Emit(OpCodes.Add); + } + else if (cselOp == CselOperation.Invert) + { + context.Emit(OpCodes.Not); + } + else if (cselOp == CselOperation.Negate) + { + context.Emit(OpCodes.Neg); + } + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblTrue); + + context.EmitLdintzr(op.Rn); + + context.MarkLabel(lblEnd); + + context.EmitStintzr(op.Rd); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitException.cs b/ChocolArm64/Instructions/InstEmitException.cs new file mode 100644 index 0000000000..8325a3978f --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitException.cs @@ -0,0 +1,86 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Brk(ILEmitterCtx context) + { + EmitExceptionCall(context, nameof(CpuThreadState.OnBreak)); + } + + public static void Svc(ILEmitterCtx context) + { + EmitExceptionCall(context, nameof(CpuThreadState.OnSvcCall)); + } + + private static void EmitExceptionCall(ILEmitterCtx context, string mthdName) + { + OpCodeException64 op = (OpCodeException64)context.CurrOp; + + context.EmitStoreState(); + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitLdc_I8(op.Position); + context.EmitLdc_I4(op.Id); + + context.EmitPrivateCall(typeof(CpuThreadState), mthdName); + + //Check if the thread should still be running, if it isn't then we return 0 + //to force a return to the dispatcher and then exit the thread. + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCallPropGet(typeof(CpuThreadState), nameof(CpuThreadState.Running)); + + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.Brtrue_S, lblEnd); + + context.EmitLdc_I8(0); + + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblEnd); + + if (context.CurrBlock.Next != null) + { + context.EmitLoadState(context.CurrBlock.Next); + } + else + { + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + } + } + + public static void Und(ILEmitterCtx context) + { + OpCode64 op = context.CurrOp; + + context.EmitStoreState(); + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitLdc_I8(op.Position); + context.EmitLdc_I4(op.RawOpCode); + + context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.OnUndefined)); + + if (context.CurrBlock.Next != null) + { + context.EmitLoadState(context.CurrBlock.Next); + } + else + { + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitFlow.cs b/ChocolArm64/Instructions/InstEmitFlow.cs new file mode 100644 index 0000000000..0e9f7cb017 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitFlow.cs @@ -0,0 +1,192 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void B(ILEmitterCtx context) + { + OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp; + + if (context.CurrBlock.Branch != null) + { + context.Emit(OpCodes.Br, context.GetLabel(op.Imm)); + } + else + { + context.EmitStoreState(); + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + + public static void B_Cond(ILEmitterCtx context) + { + OpCodeBImmCond64 op = (OpCodeBImmCond64)context.CurrOp; + + EmitBranch(context, op.Cond); + } + + public static void Bl(ILEmitterCtx context) + { + OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp; + + context.EmitLdc_I(op.Position + 4); + context.EmitStint(CpuThreadState.LrIndex); + context.EmitStoreState(); + + if (context.TryOptEmitSubroutineCall()) + { + //Note: the return value of the called method will be placed + //at the Stack, the return value is always a Int64 with the + //return address of the function. We check if the address is + //correct, if it isn't we keep returning until we reach the dispatcher. + context.Emit(OpCodes.Dup); + + context.EmitLdc_I8(op.Position + 4); + + ILLabel lblContinue = new ILLabel(); + + context.Emit(OpCodes.Beq_S, lblContinue); + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblContinue); + + context.Emit(OpCodes.Pop); + + context.EmitLoadState(context.CurrBlock.Next); + } + else + { + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + + public static void Blr(ILEmitterCtx context) + { + OpCodeBReg64 op = (OpCodeBReg64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + context.EmitSttmp(); + + context.EmitLdc_I(op.Position + 4); + context.EmitStint(CpuThreadState.LrIndex); + context.EmitStoreState(); + + context.EmitLdtmp(); + context.Emit(OpCodes.Ret); + } + + public static void Br(ILEmitterCtx context) + { + OpCodeBReg64 op = (OpCodeBReg64)context.CurrOp; + + context.EmitStoreState(); + context.EmitLdintzr(op.Rn); + + context.Emit(OpCodes.Ret); + } + + public static void Cbnz(ILEmitterCtx context) => EmitCb(context, OpCodes.Bne_Un); + public static void Cbz(ILEmitterCtx context) => EmitCb(context, OpCodes.Beq); + + private static void EmitCb(ILEmitterCtx context, OpCode ilOp) + { + OpCodeBImmCmp64 op = (OpCodeBImmCmp64)context.CurrOp; + + context.EmitLdintzr(op.Rt); + context.EmitLdc_I(0); + + EmitBranch(context, ilOp); + } + + public static void Ret(ILEmitterCtx context) + { + context.EmitStoreState(); + context.EmitLdint(CpuThreadState.LrIndex); + + context.Emit(OpCodes.Ret); + } + + public static void Tbnz(ILEmitterCtx context) => EmitTb(context, OpCodes.Bne_Un); + public static void Tbz(ILEmitterCtx context) => EmitTb(context, OpCodes.Beq); + + private static void EmitTb(ILEmitterCtx context, OpCode ilOp) + { + OpCodeBImmTest64 op = (OpCodeBImmTest64)context.CurrOp; + + context.EmitLdintzr(op.Rt); + context.EmitLdc_I(1L << op.Pos); + + context.Emit(OpCodes.And); + + context.EmitLdc_I(0); + + EmitBranch(context, ilOp); + } + + private static void EmitBranch(ILEmitterCtx context, Cond cond) + { + OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp; + + if (context.CurrBlock.Next != null && + context.CurrBlock.Branch != null) + { + context.EmitCondBranch(context.GetLabel(op.Imm), cond); + } + else + { + context.EmitStoreState(); + + ILLabel lblTaken = new ILLabel(); + + context.EmitCondBranch(lblTaken, cond); + + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblTaken); + + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + + private static void EmitBranch(ILEmitterCtx context, OpCode ilOp) + { + OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp; + + if (context.CurrBlock.Next != null && + context.CurrBlock.Branch != null) + { + context.Emit(ilOp, context.GetLabel(op.Imm)); + } + else + { + context.EmitStoreState(); + + ILLabel lblTaken = new ILLabel(); + + context.Emit(ilOp, lblTaken); + + context.EmitLdc_I8(op.Position + 4); + + context.Emit(OpCodes.Ret); + + context.MarkLabel(lblTaken); + + context.EmitLdc_I8(op.Imm); + + context.Emit(OpCodes.Ret); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitHash.cs b/ChocolArm64/Instructions/InstEmitHash.cs new file mode 100644 index 0000000000..7e21a88604 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitHash.cs @@ -0,0 +1,115 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Crc32b(ILEmitterCtx context) + { + EmitCrc32(context, nameof(SoftFallback.Crc32B)); + } + + public static void Crc32h(ILEmitterCtx context) + { + EmitCrc32(context, nameof(SoftFallback.Crc32H)); + } + + public static void Crc32w(ILEmitterCtx context) + { + EmitCrc32(context, nameof(SoftFallback.Crc32W)); + } + + public static void Crc32x(ILEmitterCtx context) + { + EmitCrc32(context, nameof(SoftFallback.Crc32X)); + } + + public static void Crc32cb(ILEmitterCtx context) + { + if (Optimizations.UseSse42) + { + EmitSse42Crc32(context, typeof(uint), typeof(byte)); + } + else + { + EmitCrc32(context, nameof(SoftFallback.Crc32Cb)); + } + } + + public static void Crc32ch(ILEmitterCtx context) + { + if (Optimizations.UseSse42) + { + EmitSse42Crc32(context, typeof(uint), typeof(ushort)); + } + else + { + EmitCrc32(context, nameof(SoftFallback.Crc32Ch)); + } + } + + public static void Crc32cw(ILEmitterCtx context) + { + if (Optimizations.UseSse42) + { + EmitSse42Crc32(context, typeof(uint), typeof(uint)); + } + else + { + EmitCrc32(context, nameof(SoftFallback.Crc32Cw)); + } + } + + public static void Crc32cx(ILEmitterCtx context) + { + if (Optimizations.UseSse42) + { + EmitSse42Crc32(context, typeof(ulong), typeof(ulong)); + } + else + { + EmitCrc32(context, nameof(SoftFallback.Crc32Cx)); + } + } + + private static void EmitSse42Crc32(ILEmitterCtx context, Type tCrc, Type tData) + { + OpCodeAluRs64 op = (OpCodeAluRs64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + context.EmitLdintzr(op.Rm); + + context.EmitCall(typeof(Sse42).GetMethod(nameof(Sse42.Crc32), new Type[] { tCrc, tData })); + + context.EmitStintzr(op.Rd); + } + + private static void EmitCrc32(ILEmitterCtx context, string name) + { + OpCodeAluRs64 op = (OpCodeAluRs64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (op.RegisterSize != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U4); + } + + context.EmitLdintzr(op.Rm); + + SoftFallback.EmitCall(context, name); + + if (op.RegisterSize != RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rd); + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitMemory.cs b/ChocolArm64/Instructions/InstEmitMemory.cs new file mode 100644 index 0000000000..96e45b3f74 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitMemory.cs @@ -0,0 +1,252 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmitMemoryHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Adr(ILEmitterCtx context) + { + OpCodeAdr64 op = (OpCodeAdr64)context.CurrOp; + + context.EmitLdc_I(op.Position + op.Imm); + context.EmitStintzr(op.Rd); + } + + public static void Adrp(ILEmitterCtx context) + { + OpCodeAdr64 op = (OpCodeAdr64)context.CurrOp; + + context.EmitLdc_I((op.Position & ~0xfffL) + (op.Imm << 12)); + context.EmitStintzr(op.Rd); + } + + public static void Ldr(ILEmitterCtx context) => EmitLdr(context, false); + public static void Ldrs(ILEmitterCtx context) => EmitLdr(context, true); + + private static void EmitLdr(ILEmitterCtx context, bool signed) + { + OpCodeMem64 op = (OpCodeMem64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + + EmitLoadAddress(context); + + if (signed && op.Extend64) + { + EmitReadSx64Call(context, op.Size); + } + else if (signed) + { + EmitReadSx32Call(context, op.Size); + } + else + { + EmitReadZxCall(context, op.Size); + } + + if (op is IOpCodeSimd64) + { + context.EmitStvec(op.Rt); + } + else + { + context.EmitStintzr(op.Rt); + } + + EmitWBackIfNeeded(context); + } + + public static void LdrLit(ILEmitterCtx context) + { + IOpCodeLit64 op = (IOpCodeLit64)context.CurrOp; + + if (op.Prefetch) + { + return; + } + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdc_I8(op.Imm); + + if (op.Signed) + { + EmitReadSx64Call(context, op.Size); + } + else + { + EmitReadZxCall(context, op.Size); + } + + if (op is IOpCodeSimd64) + { + context.EmitStvec(op.Rt); + } + else + { + context.EmitStint(op.Rt); + } + } + + public static void Ldp(ILEmitterCtx context) + { + OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp; + + void EmitReadAndStore(int rt) + { + if (op.Extend64) + { + EmitReadSx64Call(context, op.Size); + } + else + { + EmitReadZxCall(context, op.Size); + } + + if (op is IOpCodeSimd64) + { + context.EmitStvec(rt); + } + else + { + context.EmitStintzr(rt); + } + } + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + + EmitLoadAddress(context); + + EmitReadAndStore(op.Rt); + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + context.EmitLdc_I8(1 << op.Size); + + context.Emit(OpCodes.Add); + + EmitReadAndStore(op.Rt2); + + EmitWBackIfNeeded(context); + } + + public static void Str(ILEmitterCtx context) + { + OpCodeMem64 op = (OpCodeMem64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + + EmitLoadAddress(context); + + if (op is IOpCodeSimd64) + { + context.EmitLdvec(op.Rt); + } + else + { + context.EmitLdintzr(op.Rt); + } + + EmitWriteCall(context, op.Size); + + EmitWBackIfNeeded(context); + } + + public static void Stp(ILEmitterCtx context) + { + OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + + EmitLoadAddress(context); + + if (op is IOpCodeSimd64) + { + context.EmitLdvec(op.Rt); + } + else + { + context.EmitLdintzr(op.Rt); + } + + EmitWriteCall(context, op.Size); + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + context.EmitLdc_I8(1 << op.Size); + + context.Emit(OpCodes.Add); + + if (op is IOpCodeSimd64) + { + context.EmitLdvec(op.Rt2); + } + else + { + context.EmitLdintzr(op.Rt2); + } + + EmitWriteCall(context, op.Size); + + EmitWBackIfNeeded(context); + } + + private static void EmitLoadAddress(ILEmitterCtx context) + { + switch (context.CurrOp) + { + case OpCodeMemImm64 op: + context.EmitLdint(op.Rn); + + if (!op.PostIdx) + { + //Pre-indexing. + context.EmitLdc_I(op.Imm); + + context.Emit(OpCodes.Add); + } + break; + + case OpCodeMemReg64 op: + context.EmitLdint(op.Rn); + context.EmitLdintzr(op.Rm); + context.EmitCast(op.IntType); + + if (op.Shift) + { + context.EmitLsl(op.Size); + } + + context.Emit(OpCodes.Add); + break; + } + + //Save address to Scratch var since the register value may change. + context.Emit(OpCodes.Dup); + + context.EmitSttmp(); + } + + private static void EmitWBackIfNeeded(ILEmitterCtx context) + { + //Check whenever the current OpCode has post-indexed write back, if so write it. + //Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both. + if (context.CurrOp is OpCodeMemImm64 op && op.WBack) + { + context.EmitLdtmp(); + + if (op.PostIdx) + { + context.EmitLdc_I(op.Imm); + + context.Emit(OpCodes.Add); + } + + context.EmitStint(op.Rn); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitMemoryEx.cs b/ChocolArm64/Instructions/InstEmitMemoryEx.cs new file mode 100644 index 0000000000..42daca63b7 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitMemoryEx.cs @@ -0,0 +1,192 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Memory; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Threading; + +using static ChocolArm64.Instructions.InstEmitMemoryHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + [Flags] + private enum AccessType + { + None = 0, + Ordered = 1, + Exclusive = 2, + OrderedEx = Ordered | Exclusive + } + + public static void Clrex(ILEmitterCtx context) + { + EmitMemoryCall(context, nameof(MemoryManager.ClearExclusive)); + } + + public static void Dmb(ILEmitterCtx context) => EmitBarrier(context); + public static void Dsb(ILEmitterCtx context) => EmitBarrier(context); + + public static void Ldar(ILEmitterCtx context) => EmitLdr(context, AccessType.Ordered); + public static void Ldaxr(ILEmitterCtx context) => EmitLdr(context, AccessType.OrderedEx); + public static void Ldxr(ILEmitterCtx context) => EmitLdr(context, AccessType.Exclusive); + public static void Ldxp(ILEmitterCtx context) => EmitLdp(context, AccessType.Exclusive); + public static void Ldaxp(ILEmitterCtx context) => EmitLdp(context, AccessType.OrderedEx); + + private static void EmitLdr(ILEmitterCtx context, AccessType accType) + { + EmitLoad(context, accType, false); + } + + private static void EmitLdp(ILEmitterCtx context, AccessType accType) + { + EmitLoad(context, accType, true); + } + + private static void EmitLoad(ILEmitterCtx context, AccessType accType, bool pair) + { + OpCodeMemEx64 op = (OpCodeMemEx64)context.CurrOp; + + bool ordered = (accType & AccessType.Ordered) != 0; + bool exclusive = (accType & AccessType.Exclusive) != 0; + + if (ordered) + { + EmitBarrier(context); + } + + if (exclusive) + { + EmitMemoryCall(context, nameof(MemoryManager.SetExclusive), op.Rn); + } + + context.EmitLdint(op.Rn); + context.EmitSttmp(); + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + + EmitReadZxCall(context, op.Size); + + context.EmitStintzr(op.Rt); + + if (pair) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdtmp(); + context.EmitLdc_I8(1 << op.Size); + + context.Emit(OpCodes.Add); + + EmitReadZxCall(context, op.Size); + + context.EmitStintzr(op.Rt2); + } + } + + public static void Pfrm(ILEmitterCtx context) + { + //Memory Prefetch, execute as no-op. + } + + public static void Stlr(ILEmitterCtx context) => EmitStr(context, AccessType.Ordered); + public static void Stlxr(ILEmitterCtx context) => EmitStr(context, AccessType.OrderedEx); + public static void Stxr(ILEmitterCtx context) => EmitStr(context, AccessType.Exclusive); + public static void Stxp(ILEmitterCtx context) => EmitStp(context, AccessType.Exclusive); + public static void Stlxp(ILEmitterCtx context) => EmitStp(context, AccessType.OrderedEx); + + private static void EmitStr(ILEmitterCtx context, AccessType accType) + { + EmitStore(context, accType, false); + } + + private static void EmitStp(ILEmitterCtx context, AccessType accType) + { + EmitStore(context, accType, true); + } + + private static void EmitStore(ILEmitterCtx context, AccessType accType, bool pair) + { + OpCodeMemEx64 op = (OpCodeMemEx64)context.CurrOp; + + bool ordered = (accType & AccessType.Ordered) != 0; + bool exclusive = (accType & AccessType.Exclusive) != 0; + + if (ordered) + { + EmitBarrier(context); + } + + ILLabel lblEx = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + if (exclusive) + { + EmitMemoryCall(context, nameof(MemoryManager.TestExclusive), op.Rn); + + context.Emit(OpCodes.Brtrue_S, lblEx); + + context.EmitLdc_I8(1); + context.EmitStintzr(op.Rs); + + context.Emit(OpCodes.Br_S, lblEnd); + } + + context.MarkLabel(lblEx); + + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(op.Rn); + context.EmitLdintzr(op.Rt); + + EmitWriteCall(context, op.Size); + + if (pair) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(op.Rn); + context.EmitLdc_I8(1 << op.Size); + + context.Emit(OpCodes.Add); + + context.EmitLdintzr(op.Rt2); + + EmitWriteCall(context, op.Size); + } + + if (exclusive) + { + context.EmitLdc_I8(0); + context.EmitStintzr(op.Rs); + + EmitMemoryCall(context, nameof(MemoryManager.ClearExclusiveForStore)); + } + + context.MarkLabel(lblEnd); + } + + private static void EmitMemoryCall(ILEmitterCtx context, string name, int rn = -1) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCallPropGet(typeof(CpuThreadState), nameof(CpuThreadState.Core)); + + if (rn != -1) + { + context.EmitLdint(rn); + } + + context.EmitCall(typeof(MemoryManager), name); + } + + private static void EmitBarrier(ILEmitterCtx context) + { + //Note: This barrier is most likely not necessary, and probably + //doesn't make any difference since we need to do a ton of stuff + //(software MMU emulation) to read or write anything anyway. + context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier)); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitMemoryHelper.cs b/ChocolArm64/Instructions/InstEmitMemoryHelper.cs new file mode 100644 index 0000000000..f953564c46 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitMemoryHelper.cs @@ -0,0 +1,138 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Memory; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static class InstEmitMemoryHelper + { + private enum Extension + { + Zx, + Sx32, + Sx64 + } + + public static void EmitReadZxCall(ILEmitterCtx context, int size) + { + EmitReadCall(context, Extension.Zx, size); + } + + public static void EmitReadSx32Call(ILEmitterCtx context, int size) + { + EmitReadCall(context, Extension.Sx32, size); + } + + public static void EmitReadSx64Call(ILEmitterCtx context, int size) + { + EmitReadCall(context, Extension.Sx64, size); + } + + private static void EmitReadCall(ILEmitterCtx context, Extension ext, int size) + { + bool isSimd = GetIsSimd(context); + + string name = null; + + if (size < 0 || size > (isSimd ? 4 : 3)) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + if (isSimd) + { + switch (size) + { + case 0: name = nameof(MemoryManager.ReadVector8); break; + case 1: name = nameof(MemoryManager.ReadVector16); break; + case 2: name = nameof(MemoryManager.ReadVector32); break; + case 3: name = nameof(MemoryManager.ReadVector64); break; + case 4: name = nameof(MemoryManager.ReadVector128); break; + } + } + else + { + switch (size) + { + case 0: name = nameof(MemoryManager.ReadByte); break; + case 1: name = nameof(MemoryManager.ReadUInt16); break; + case 2: name = nameof(MemoryManager.ReadUInt32); break; + case 3: name = nameof(MemoryManager.ReadUInt64); break; + } + } + + context.EmitCall(typeof(MemoryManager), name); + + if (!isSimd) + { + if (ext == Extension.Sx32 || + ext == Extension.Sx64) + { + switch (size) + { + case 0: context.Emit(OpCodes.Conv_I1); break; + case 1: context.Emit(OpCodes.Conv_I2); break; + case 2: context.Emit(OpCodes.Conv_I4); break; + } + } + + if (size < 3) + { + context.Emit(ext == Extension.Sx64 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + } + } + + public static void EmitWriteCall(ILEmitterCtx context, int size) + { + bool isSimd = GetIsSimd(context); + + string name = null; + + if (size < 0 || size > (isSimd ? 4 : 3)) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + if (size < 3 && !isSimd) + { + context.Emit(OpCodes.Conv_I4); + } + + if (isSimd) + { + switch (size) + { + case 0: name = nameof(MemoryManager.WriteVector8); break; + case 1: name = nameof(MemoryManager.WriteVector16); break; + case 2: name = nameof(MemoryManager.WriteVector32); break; + case 3: name = nameof(MemoryManager.WriteVector64); break; + case 4: name = nameof(MemoryManager.WriteVector128); break; + } + } + else + { + switch (size) + { + case 0: name = nameof(MemoryManager.WriteByte); break; + case 1: name = nameof(MemoryManager.WriteUInt16); break; + case 2: name = nameof(MemoryManager.WriteUInt32); break; + case 3: name = nameof(MemoryManager.WriteUInt64); break; + } + } + + context.EmitCall(typeof(MemoryManager), name); + } + + private static bool GetIsSimd(ILEmitterCtx context) + { + return context.CurrOp is IOpCodeSimd64 && + !(context.CurrOp is OpCodeSimdMemMs64 || + context.CurrOp is OpCodeSimdMemSs64); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitMove.cs b/ChocolArm64/Instructions/InstEmitMove.cs new file mode 100644 index 0000000000..be3e8e2d31 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitMove.cs @@ -0,0 +1,41 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Movk(ILEmitterCtx context) + { + OpCodeMov64 op = (OpCodeMov64)context.CurrOp; + + context.EmitLdintzr(op.Rd); + context.EmitLdc_I(~(0xffffL << op.Pos)); + + context.Emit(OpCodes.And); + + context.EmitLdc_I(op.Imm); + + context.Emit(OpCodes.Or); + + context.EmitStintzr(op.Rd); + } + + public static void Movn(ILEmitterCtx context) + { + OpCodeMov64 op = (OpCodeMov64)context.CurrOp; + + context.EmitLdc_I(~op.Imm); + context.EmitStintzr(op.Rd); + } + + public static void Movz(ILEmitterCtx context) + { + OpCodeMov64 op = (OpCodeMov64)context.CurrOp; + + context.EmitLdc_I(op.Imm); + context.EmitStintzr(op.Rd); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitMul.cs b/ChocolArm64/Instructions/InstEmitMul.cs new file mode 100644 index 0000000000..b7418e6922 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitMul.cs @@ -0,0 +1,80 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Madd(ILEmitterCtx context) => EmitMul(context, OpCodes.Add); + public static void Msub(ILEmitterCtx context) => EmitMul(context, OpCodes.Sub); + + private static void EmitMul(ILEmitterCtx context, OpCode ilOp) + { + OpCodeMul64 op = (OpCodeMul64)context.CurrOp; + + context.EmitLdintzr(op.Ra); + context.EmitLdintzr(op.Rn); + context.EmitLdintzr(op.Rm); + + context.Emit(OpCodes.Mul); + context.Emit(ilOp); + + context.EmitStintzr(op.Rd); + } + + public static void Smaddl(ILEmitterCtx context) => EmitMull(context, OpCodes.Add, true); + public static void Smsubl(ILEmitterCtx context) => EmitMull(context, OpCodes.Sub, true); + public static void Umaddl(ILEmitterCtx context) => EmitMull(context, OpCodes.Add, false); + public static void Umsubl(ILEmitterCtx context) => EmitMull(context, OpCodes.Sub, false); + + private static void EmitMull(ILEmitterCtx context, OpCode addSubOp, bool signed) + { + OpCodeMul64 op = (OpCodeMul64)context.CurrOp; + + OpCode castOp = signed + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8; + + context.EmitLdintzr(op.Ra); + context.EmitLdintzr(op.Rn); + + context.Emit(OpCodes.Conv_I4); + context.Emit(castOp); + + context.EmitLdintzr(op.Rm); + + context.Emit(OpCodes.Conv_I4); + context.Emit(castOp); + context.Emit(OpCodes.Mul); + + context.Emit(addSubOp); + + context.EmitStintzr(op.Rd); + } + + public static void Smulh(ILEmitterCtx context) + { + OpCodeMul64 op = (OpCodeMul64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + context.EmitLdintzr(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.SMulHi128)); + + context.EmitStintzr(op.Rd); + } + + public static void Umulh(ILEmitterCtx context) + { + OpCodeMul64 op = (OpCodeMul64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + context.EmitLdintzr(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.UMulHi128)); + + context.EmitStintzr(op.Rd); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs b/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs new file mode 100644 index 0000000000..c05e9f9465 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs @@ -0,0 +1,3013 @@ +// https://github.com/intel/ARM_NEON_2_x86_SSE/blob/master/NEON_2_SSE.h + +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Abs_S(ILEmitterCtx context) + { + EmitScalarUnaryOpSx(context, () => EmitAbs(context)); + } + + public static void Abs_V(ILEmitterCtx context) + { + EmitVectorUnaryOpSx(context, () => EmitAbs(context)); + } + + public static void Add_S(ILEmitterCtx context) + { + EmitScalarBinaryOpZx(context, () => context.Emit(OpCodes.Add)); + } + + public static void Add_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.Add)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Add)); + } + } + + public static void Addhn_V(ILEmitterCtx context) + { + EmitHighNarrow(context, () => context.Emit(OpCodes.Add), round: false); + } + + public static void Addp_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, 0, op.Size); + EmitVectorExtractZx(context, op.Rn, 1, op.Size); + + context.Emit(OpCodes.Add); + + EmitScalarSet(context, op.Rd, op.Size); + } + + public static void Addp_V(ILEmitterCtx context) + { + EmitVectorPairwiseOpZx(context, () => context.Emit(OpCodes.Add)); + } + + public static void Addv_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + EmitVectorExtractZx(context, op.Rn, 0, op.Size); + + for (int index = 1; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.Emit(OpCodes.Add); + } + + EmitScalarSet(context, op.Rd, op.Size); + } + + public static void Cls_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + int eSize = 8 << op.Size; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.EmitLdc_I4(eSize); + + SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingSigns)); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Clz_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + int eSize = 8 << op.Size; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + if (Lzcnt.IsSupported && eSize == 32) + { + context.Emit(OpCodes.Conv_U4); + + context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { typeof(uint) })); + + context.Emit(OpCodes.Conv_U8); + } + else + { + context.EmitLdc_I4(eSize); + + SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingZeros)); + } + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Cnt_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, 0); + + if (Popcnt.IsSupported) + { + context.EmitCall(typeof(Popcnt).GetMethod(nameof(Popcnt.PopCount), new Type[] { typeof(ulong) })); + } + else + { + SoftFallback.EmitCall(context, nameof(SoftFallback.CountSetBits8)); + } + + EmitVectorInsert(context, op.Rd, index, 0); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Fabd_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + context.Emit(OpCodes.Sub); + + EmitUnaryMathCall(context, nameof(Math.Abs)); + }); + } + + public static void Fabs_S(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + if (op.Size == 0) + { + Type[] typesSsv = new Type[] { typeof(float) }; + Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(-0f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesAndNot)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (op.Size == 1) */ + { + Type[] typesSsv = new Type[] { typeof(double) }; + Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(-0d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); + + EmitLdvecWithCastToDouble(context, op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndNot)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Abs)); + }); + } + } + + public static void Fabs_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSav = new Type[] { typeof(float) }; + Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(-0f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesAndNot)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesSav = new Type[] { typeof(double) }; + Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(-0d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + EmitLdvecWithCastToDouble(context, op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndNot)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Abs)); + }); + } + } + + public static void Fadd_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.AddScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd)); + }); + } + } + + public static void Fadd_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Add)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd)); + }); + } + } + + public static void Faddp_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + EmitVectorExtractF(context, op.Rn, 1, sizeF); + + context.Emit(OpCodes.Add); + + EmitScalarSetF(context, op.Rd, sizeF); + } + + public static void Faddp_V(ILEmitterCtx context) + { + EmitVectorPairwiseOpF(context, () => context.Emit(OpCodes.Add)); + } + + public static void Fdiv_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.DivideScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPDiv)); + }); + } + } + + public static void Fdiv_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Divide)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPDiv)); + }); + } + } + + public static void Fmadd_S(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (op.Size == 0) + { + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Ra); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulAdd)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AddScalar), typesMulAdd)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (op.Size == 1) */ + { + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Ra); + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AddScalar), typesMulAdd)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarTernaryRaOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd)); + }); + } + } + + public static void Fmax_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.MaxScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax)); + }); + } + } + + public static void Fmax_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Max)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax)); + }); + } + } + + public static void Fmaxnm_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum)); + }); + } + + public static void Fmaxnm_V(ILEmitterCtx context) + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMaxNum)); + }); + } + + public static void Fmaxp_V(ILEmitterCtx context) + { + EmitVectorPairwiseOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax)); + }); + } + + public static void Fmin_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.MinScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin)); + }); + } + } + + public static void Fmin_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Min)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin)); + }); + } + } + + public static void Fminnm_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum)); + }); + } + + public static void Fminnm_V(ILEmitterCtx context) + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMinNum)); + }); + } + + public static void Fminp_V(ILEmitterCtx context) + { + EmitVectorPairwiseOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin)); + }); + } + + public static void Fmla_Se(ILEmitterCtx context) + { + EmitScalarTernaryOpByElemF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + + public static void Fmla_V(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulAdd)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Add), typesMulAdd)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Rd); + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorTernaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd)); + }); + } + } + + public static void Fmla_Ve(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Rd); + + context.EmitLdvec(op.Rn); + + context.EmitLdvec(op.Rm); + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Shuffle), typesSfl)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulAdd)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Add), typesMulAdd)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Rd); + + EmitLdvecWithCastToDouble(context, op.Rn); + + EmitLdvecWithCastToDouble(context, op.Rm); + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(op.Index | op.Index << 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Shuffle), typesSfl)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulAdd)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorTernaryOpByElemF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd)); + }); + } + } + + public static void Fmls_Se(ILEmitterCtx context) + { + EmitScalarTernaryOpByElemF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + + public static void Fmls_V(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesMulSub)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Rd); + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorTernaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub)); + }); + } + } + + public static void Fmls_Ve(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Rd); + + context.EmitLdvec(op.Rn); + + context.EmitLdvec(op.Rm); + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Shuffle), typesSfl)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesMulSub)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Rd); + + EmitLdvecWithCastToDouble(context, op.Rn); + + EmitLdvecWithCastToDouble(context, op.Rm); + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(op.Index | op.Index << 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Shuffle), typesSfl)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorTernaryOpByElemF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub)); + }); + } + } + + public static void Fmsub_S(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (op.Size == 0) + { + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Ra); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), typesMulSub)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (op.Size == 1) */ + { + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Ra); + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarTernaryRaOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub)); + }); + } + } + + public static void Fmul_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.MultiplyScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul)); + }); + } + } + + public static void Fmul_Se(ILEmitterCtx context) + { + EmitScalarBinaryOpByElemF(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Fmul_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Multiply)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul)); + }); + } + } + + public static void Fmul_Ve(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; + Type[] typesMul = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Rn); + + context.EmitLdvec(op.Rm); + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Shuffle), typesSfl)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMul)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; + Type[] typesMul = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(context, op.Rn); + + EmitLdvecWithCastToDouble(context, op.Rm); + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(op.Index | op.Index << 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Shuffle), typesSfl)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMul)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpByElemF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul)); + }); + } + } + + public static void Fmulx_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX)); + }); + } + + public static void Fmulx_Se(ILEmitterCtx context) + { + EmitScalarBinaryOpByElemF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX)); + }); + } + + public static void Fmulx_V(ILEmitterCtx context) + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX)); + }); + } + + public static void Fmulx_Ve(ILEmitterCtx context) + { + EmitVectorBinaryOpByElemF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulX)); + }); + } + + public static void Fneg_S(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + if (op.Size == 0) + { + Type[] typesSsv = new Type[] { typeof(float) }; + Type[] typesXor = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(-0f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Xor), typesXor)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (op.Size == 1) */ + { + Type[] typesSsv = new Type[] { typeof(double) }; + Type[] typesXor = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(-0d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); + + EmitLdvecWithCastToDouble(context, op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXor)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarUnaryOpF(context, () => context.Emit(OpCodes.Neg)); + } + } + + public static void Fneg_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSav = new Type[] { typeof(float) }; + Type[] typesXor = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(-0f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Xor), typesXor)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesSav = new Type[] { typeof(double) }; + Type[] typesXor = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(-0d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + EmitLdvecWithCastToDouble(context, op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXor)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorUnaryOpF(context, () => context.Emit(OpCodes.Neg)); + } + } + + public static void Fnmadd_S(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + + context.Emit(OpCodes.Neg); + + EmitVectorExtractF(context, op.Rm, 0, sizeF); + + context.Emit(OpCodes.Mul); + + EmitVectorExtractF(context, op.Ra, 0, sizeF); + + context.Emit(OpCodes.Sub); + + EmitScalarSetF(context, op.Rd, sizeF); + } + + public static void Fnmsub_S(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + EmitVectorExtractF(context, op.Rm, 0, sizeF); + + context.Emit(OpCodes.Mul); + + EmitVectorExtractF(context, op.Ra, 0, sizeF); + + context.Emit(OpCodes.Sub); + + EmitScalarSetF(context, op.Rd, sizeF); + } + + public static void Fnmul_S(ILEmitterCtx context) + { + EmitScalarBinaryOpF(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Neg); + }); + } + + public static void Frecpe_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnarySoftFloatCall(context, nameof(SoftFloat.RecipEstimate)); + }); + } + + public static void Frecpe_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnarySoftFloatCall(context, nameof(SoftFloat.RecipEstimate)); + }); + } + + public static void Frecps_S(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSsv = new Type[] { typeof(float) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(2f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), typesMulSub)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (sizeF == 1) */ + { + Type[] typesSsv = new Type[] { typeof(double) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(2d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); + + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipStepFused)); + }); + } + } + + public static void Frecps_V(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSav = new Type[] { typeof(float) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(2f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesMulSub)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesSav = new Type[] { typeof(double) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(2d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecipStepFused)); + }); + } + } + + public static void Frecpx_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPRecpX)); + }); + } + + public static void Frinta_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + EmitRoundMathCall(context, MidpointRounding.AwayFromZero); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Frinta_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitRoundMathCall(context, MidpointRounding.AwayFromZero); + }); + } + + public static void Frinti_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitScalarUnaryOpF(context, () => + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (op.Size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.RoundF)); + } + else if (op.Size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.Round)); + } + else + { + throw new InvalidOperationException(); + } + }); + } + + public static void Frinti_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + EmitVectorUnaryOpF(context, () => + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.RoundF)); + } + else if (sizeF == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.Round)); + } + else + { + throw new InvalidOperationException(); + } + }); + } + + public static void Frintm_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Floor)); + }); + } + + public static void Frintm_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Floor)); + }); + } + + public static void Frintn_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + EmitRoundMathCall(context, MidpointRounding.ToEven); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Frintn_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitRoundMathCall(context, MidpointRounding.ToEven); + }); + } + + public static void Frintp_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Ceiling)); + }); + } + + public static void Frintp_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnaryMathCall(context, nameof(Math.Ceiling)); + }); + } + + public static void Frintx_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitScalarUnaryOpF(context, () => + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (op.Size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.RoundF)); + } + else if (op.Size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.Round)); + } + else + { + throw new InvalidOperationException(); + } + }); + } + + public static void Frintx_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorUnaryOpF(context, () => + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (op.Size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.RoundF)); + } + else if (op.Size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.Round)); + } + else + { + throw new InvalidOperationException(); + } + }); + } + + public static void Frsqrte_S(ILEmitterCtx context) + { + EmitScalarUnaryOpF(context, () => + { + EmitUnarySoftFloatCall(context, nameof(SoftFloat.InvSqrtEstimate)); + }); + } + + public static void Frsqrte_V(ILEmitterCtx context) + { + EmitVectorUnaryOpF(context, () => + { + EmitUnarySoftFloatCall(context, nameof(SoftFloat.InvSqrtEstimate)); + }); + } + + public static void Frsqrts_S(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSsv = new Type[] { typeof(float) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(0.5f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); + + context.EmitLdc_R4(3f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), typesMulSub)); + + context.EmitStvec(op.Rd); + + EmitVectorZero32_128(context, op.Rd); + } + else /* if (sizeF == 1) */ + { + Type[] typesSsv = new Type[] { typeof(double) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(0.5d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); + + context.EmitLdc_R8(3d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); + + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPRSqrtStepFused)); + }); + } + } + + public static void Frsqrts_V(ILEmitterCtx context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] typesSav = new Type[] { typeof(float) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R4(0.5f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); + + context.EmitLdc_R4(3f); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesMulSub)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] typesSav = new Type[] { typeof(double) }; + Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdc_R8(0.5d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitLdc_R8(3d); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + EmitLdvecWithCastToDouble(context, op.Rn); + EmitLdvecWithCastToDouble(context, op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPRSqrtStepFused)); + }); + } + } + + public static void Fsqrt_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.SqrtScalar)); + } + else + { + EmitScalarUnaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPSqrt)); + }); + } + } + + public static void Fsqrt_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Sqrt)); + } + else + { + EmitVectorUnaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPSqrt)); + }); + } + } + + public static void Fsub_S(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.SubtractScalar)); + } + else + { + EmitScalarBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub)); + }); + } + } + + public static void Fsub_V(ILEmitterCtx context) + { + if (Optimizations.FastFP && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.Subtract)); + } + else + { + EmitVectorBinaryOpF(context, () => + { + EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub)); + }); + } + } + + public static void Mla_V(ILEmitterCtx context) + { + EmitVectorTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + + public static void Mla_Ve(ILEmitterCtx context) + { + EmitVectorTernaryOpByElemZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + + public static void Mls_V(ILEmitterCtx context) + { + EmitVectorTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + + public static void Mls_Ve(ILEmitterCtx context) + { + EmitVectorTernaryOpByElemZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + + public static void Mul_V(ILEmitterCtx context) + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Mul_Ve(ILEmitterCtx context) + { + EmitVectorBinaryOpByElemZx(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Neg_S(ILEmitterCtx context) + { + EmitScalarUnaryOpSx(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Neg_V(ILEmitterCtx context) + { + EmitVectorUnaryOpSx(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Raddhn_V(ILEmitterCtx context) + { + EmitHighNarrow(context, () => context.Emit(OpCodes.Add), round: true); + } + + public static void Rsubhn_V(ILEmitterCtx context) + { + EmitHighNarrow(context, () => context.Emit(OpCodes.Sub), round: true); + } + + public static void Saba_V(ILEmitterCtx context) + { + EmitVectorTernaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + + context.Emit(OpCodes.Add); + }); + } + + public static void Sabal_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmTernaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + + context.Emit(OpCodes.Add); + }); + } + + public static void Sabd_V(ILEmitterCtx context) + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } + + public static void Sabdl_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmBinaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } + + public static void Sadalp_V(ILEmitterCtx context) + { + EmitAddLongPairwise(context, signed: true, accumulate: true); + } + + public static void Saddl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Add)); + } + } + + public static void Saddlp_V(ILEmitterCtx context) + { + EmitAddLongPairwise(context, signed: true, accumulate: false); + } + + public static void Saddw_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size + 1); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRmBinaryOpSx(context, () => context.Emit(OpCodes.Add)); + } + } + + public static void Shadd_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAndXorAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndXorAdd)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesAndXorAdd)); + + context.EmitLdc_I4(1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAndXorAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + }); + } + } + + public static void Shsub_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size < 2) + { + Type[] typesSav = new Type[] { IntTypesPerSizeLog2[op.Size] }; + Type[] typesAddSub = new Type[] { VectorIntTypesPerSizeLog2 [op.Size], VectorIntTypesPerSizeLog2 [op.Size] }; + Type[] typesAvg = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + context.EmitLdc_I4(op.Size == 0 ? sbyte.MinValue : short.MinValue); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitStvectmp(); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAddSub)); + + context.Emit(OpCodes.Dup); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAddSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesAddSub)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + }); + } + } + + public static void Smax_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesMax = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + Type typeSse = op.Size == 1 ? typeof(Sse2) : typeof(Sse41); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeSse.GetMethod(nameof(Sse2.Max), typesMax)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + Type[] types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Max), types); + + EmitVectorBinaryOpSx(context, () => context.EmitCall(mthdInfo)); + } + } + + public static void Smaxp_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Max), types); + + EmitVectorPairwiseOpSx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Smin_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesMin = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + Type typeSse = op.Size == 1 ? typeof(Sse2) : typeof(Sse41); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeSse.GetMethod(nameof(Sse2.Min), typesMin)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + Type[] types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Min), types); + + EmitVectorBinaryOpSx(context, () => context.EmitCall(mthdInfo)); + } + } + + public static void Sminp_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Min), types); + + EmitVectorPairwiseOpSx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Smlal_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) + { + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesMulAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rd, op.Size + 1); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitCall(typeSse.GetMethod(nameof(Sse2.MultiplyLow), typesMulAdd)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmTernaryOpSx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + } + + public static void Smlsl_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) + { + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesMulSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rd, op.Size + 1); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitCall(typeSse.GetMethod(nameof(Sse2.MultiplyLow), typesMulSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmTernaryOpSx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + } + + public static void Smull_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Sqabs_S(ILEmitterCtx context) + { + EmitScalarSaturatingUnaryOpSx(context, () => EmitAbs(context)); + } + + public static void Sqabs_V(ILEmitterCtx context) + { + EmitVectorSaturatingUnaryOpSx(context, () => EmitAbs(context)); + } + + public static void Sqadd_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpSx(context, SaturatingFlags.Add); + } + + public static void Sqadd_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpSx(context, SaturatingFlags.Add); + } + + public static void Sqdmulh_S(ILEmitterCtx context) + { + EmitSaturatingBinaryOp(context, () => EmitDoublingMultiplyHighHalf(context, round: false), SaturatingFlags.ScalarSx); + } + + public static void Sqdmulh_V(ILEmitterCtx context) + { + EmitSaturatingBinaryOp(context, () => EmitDoublingMultiplyHighHalf(context, round: false), SaturatingFlags.VectorSx); + } + + public static void Sqneg_S(ILEmitterCtx context) + { + EmitScalarSaturatingUnaryOpSx(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Sqneg_V(ILEmitterCtx context) + { + EmitVectorSaturatingUnaryOpSx(context, () => context.Emit(OpCodes.Neg)); + } + + public static void Sqrdmulh_S(ILEmitterCtx context) + { + EmitSaturatingBinaryOp(context, () => EmitDoublingMultiplyHighHalf(context, round: true), SaturatingFlags.ScalarSx); + } + + public static void Sqrdmulh_V(ILEmitterCtx context) + { + EmitSaturatingBinaryOp(context, () => EmitDoublingMultiplyHighHalf(context, round: true), SaturatingFlags.VectorSx); + } + + public static void Sqsub_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpSx(context, SaturatingFlags.Sub); + } + + public static void Sqsub_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpSx(context, SaturatingFlags.Sub); + } + + public static void Sqxtn_S(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarSxSx); + } + + public static void Sqxtn_V(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorSxSx); + } + + public static void Sqxtun_S(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarSxZx); + } + + public static void Sqxtun_V(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorSxZx); + } + + public static void Srhadd_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size < 2) + { + Type[] typesSav = new Type[] { IntTypesPerSizeLog2[op.Size] }; + Type[] typesSubAdd = new Type[] { VectorIntTypesPerSizeLog2 [op.Size], VectorIntTypesPerSizeLog2 [op.Size] }; + Type[] typesAvg = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + context.EmitLdc_I4(op.Size == 0 ? sbyte.MinValue : short.MinValue); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSubAdd)); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSubAdd)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesSubAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr); + }); + } + } + + public static void Ssubl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Sub)); + } + } + + public static void Ssubw_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size + 1); + + EmitLdvecWithSignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRmBinaryOpSx(context, () => context.Emit(OpCodes.Sub)); + } + } + + public static void Sub_S(ILEmitterCtx context) + { + EmitScalarBinaryOpZx(context, () => context.Emit(OpCodes.Sub)); + } + + public static void Sub_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.Subtract)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Sub)); + } + } + + public static void Subhn_V(ILEmitterCtx context) + { + EmitHighNarrow(context, () => context.Emit(OpCodes.Sub), round: false); + } + + public static void Suqadd_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpSx(context, SaturatingFlags.Accumulate); + } + + public static void Suqadd_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpSx(context, SaturatingFlags.Accumulate); + } + + public static void Uaba_V(ILEmitterCtx context) + { + EmitVectorTernaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + + context.Emit(OpCodes.Add); + }); + } + + public static void Uabal_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmTernaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + + context.Emit(OpCodes.Add); + }); + } + + public static void Uabd_V(ILEmitterCtx context) + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } + + public static void Uabdl_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmBinaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } + + public static void Uadalp_V(ILEmitterCtx context) + { + EmitAddLongPairwise(context, signed: false, accumulate: true); + } + + public static void Uaddl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], + VectorUIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Add)); + } + } + + public static void Uaddlp_V(ILEmitterCtx context) + { + EmitAddLongPairwise(context, signed: false, accumulate: false); + } + + public static void Uaddlv_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + EmitVectorExtractZx(context, op.Rn, 0, op.Size); + + for (int index = 1; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.Emit(OpCodes.Add); + } + + EmitScalarSet(context, op.Rd, op.Size + 1); + } + + public static void Uaddw_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], + VectorUIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size + 1); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRmBinaryOpZx(context, () => context.Emit(OpCodes.Add)); + } + } + + public static void Uhadd_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAndXorAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndXorAdd)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesAndXorAdd)); + + context.EmitLdc_I4(1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAndXorAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr_Un); + }); + } + } + + public static void Uhsub_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size < 2) + { + Type[] typesAvgSub = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + context.Emit(OpCodes.Dup); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvgSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesAvgSub)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr_Un); + }); + } + } + + public static void Umax_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesMax = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeSse.GetMethod(nameof(Sse2.Max), typesMax)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + Type[] types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Max), types); + + EmitVectorBinaryOpZx(context, () => context.EmitCall(mthdInfo)); + } + } + + public static void Umaxp_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Max), types); + + EmitVectorPairwiseOpZx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Umin_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesMin = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeSse.GetMethod(nameof(Sse2.Min), typesMin)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + Type[] types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Min), types); + + EmitVectorBinaryOpZx(context, () => context.EmitCall(mthdInfo)); + } + } + + public static void Uminp_V(ILEmitterCtx context) + { + Type[] types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo mthdInfo = typeof(Math).GetMethod(nameof(Math.Min), types); + + EmitVectorPairwiseOpZx(context, () => context.EmitCall(mthdInfo)); + } + + public static void Umlal_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesMulAdd = new Type[] { VectorIntTypesPerSizeLog2 [op.Size + 1], + VectorIntTypesPerSizeLog2 [op.Size + 1] }; + + Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size + 1); + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitCall(typeSse.GetMethod(nameof(Sse2.MultiplyLow), typesMulAdd)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } + } + + public static void Umlsl_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesMulSub = new Type[] { VectorIntTypesPerSizeLog2 [op.Size + 1], + VectorIntTypesPerSizeLog2 [op.Size + 1] }; + + Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size + 1); + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitCall(typeSse.GetMethod(nameof(Sse2.MultiplyLow), typesMulSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } + } + + public static void Umull_V(ILEmitterCtx context) + { + EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Mul)); + } + + public static void Uqadd_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Add); + } + + public static void Uqadd_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Add); + } + + public static void Uqsub_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Sub); + } + + public static void Uqsub_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Sub); + } + + public static void Uqxtn_S(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.ScalarZxZx); + } + + public static void Uqxtn_V(ILEmitterCtx context) + { + EmitSaturatingNarrowOp(context, SaturatingNarrowFlags.VectorZxZx); + } + + public static void Urhadd_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size < 2) + { + Type[] typesAvg = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Add); + + context.Emit(OpCodes.Ldc_I4_1); + context.Emit(OpCodes.Shr_Un); + }); + } + } + + public static void Usqadd_S(ILEmitterCtx context) + { + EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Accumulate); + } + + public static void Usqadd_V(ILEmitterCtx context) + { + EmitVectorSaturatingBinaryOpZx(context, SaturatingFlags.Accumulate); + } + + public static void Usubl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesSub = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], + VectorUIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Sub)); + } + } + + public static void Usubw_V(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesSub = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], + VectorUIntTypesPerSizeLog2[op.Size + 1] }; + + string[] namesCvt = new string[] { nameof(Sse41.ConvertToVector128Int16), + nameof(Sse41.ConvertToVector128Int32), + nameof(Sse41.ConvertToVector128Int64) }; + + int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size + 1); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitLdc_I4(numBytes); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + + context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + } + else + { + EmitVectorWidenRmBinaryOpZx(context, () => context.Emit(OpCodes.Sub)); + } + } + + private static void EmitAbs(ILEmitterCtx context) + { + ILLabel lblTrue = new ILLabel(); + + context.Emit(OpCodes.Dup); + context.Emit(OpCodes.Ldc_I4_0); + context.Emit(OpCodes.Bge_S, lblTrue); + + context.Emit(OpCodes.Neg); + + context.MarkLabel(lblTrue); + } + + private static void EmitAddLongPairwise(ILEmitterCtx context, bool signed, bool accumulate) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtract(context, op.Rn, idx, op.Size, signed); + EmitVectorExtract(context, op.Rn, idx + 1, op.Size, signed); + + context.Emit(OpCodes.Add); + + if (accumulate) + { + EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); + + context.Emit(OpCodes.Add); + } + + EmitVectorInsertTmp(context, index, op.Size + 1); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitDoublingMultiplyHighHalf(ILEmitterCtx context, bool round) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int eSize = 8 << op.Size; + + context.Emit(OpCodes.Mul); + + if (!round) + { + context.EmitAsr(eSize - 1); + } + else + { + long roundConst = 1L << (eSize - 1); + + ILLabel lblTrue = new ILLabel(); + + context.EmitLsl(1); + + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + + context.EmitAsr(eSize); + + context.Emit(OpCodes.Dup); + context.EmitLdc_I8((long)int.MinValue); + context.Emit(OpCodes.Bne_Un_S, lblTrue); + + context.Emit(OpCodes.Neg); + + context.MarkLabel(lblTrue); + } + } + + private static void EmitHighNarrow(ILEmitterCtx context, Action emit, bool round) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int elems = 8 >> op.Size; + + int eSize = 8 << op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + long roundConst = 1L << (eSize - 1); + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size + 1); + EmitVectorExtractZx(context, op.Rm, index, op.Size + 1); + + emit(); + + if (round) + { + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + } + + context.EmitLsr(eSize); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdCmp.cs b/ChocolArm64/Instructions/InstEmitSimdCmp.cs new file mode 100644 index 0000000000..3ee2548248 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdCmp.cs @@ -0,0 +1,615 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitAluHelper; +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Cmeq_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Beq_S, scalar: true); + } + + public static void Cmeq_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 op) + { + if (op.Size < 3 && Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.CompareEqual)); + } + else if (op.Size == 3 && Optimizations.UseSse41) + { + EmitSse41Op(context, nameof(Sse41.CompareEqual)); + } + else + { + EmitCmp(context, OpCodes.Beq_S, scalar: false); + } + } + else + { + EmitCmp(context, OpCodes.Beq_S, scalar: false); + } + } + + public static void Cmge_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bge_S, scalar: true); + } + + public static void Cmge_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bge_S, scalar: false); + } + + public static void Cmgt_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bgt_S, scalar: true); + } + + public static void Cmgt_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 op) + { + if (op.Size < 3 && Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.CompareGreaterThan)); + } + else if (op.Size == 3 && Optimizations.UseSse42) + { + EmitSse42Op(context, nameof(Sse42.CompareGreaterThan)); + } + else + { + EmitCmp(context, OpCodes.Bgt_S, scalar: false); + } + } + else + { + EmitCmp(context, OpCodes.Bgt_S, scalar: false); + } + } + + public static void Cmhi_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bgt_Un_S, scalar: true); + } + + public static void Cmhi_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bgt_Un_S, scalar: false); + } + + public static void Cmhs_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bge_Un_S, scalar: true); + } + + public static void Cmhs_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Bge_Un_S, scalar: false); + } + + public static void Cmle_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Ble_S, scalar: true); + } + + public static void Cmle_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Ble_S, scalar: false); + } + + public static void Cmlt_S(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Blt_S, scalar: true); + } + + public static void Cmlt_V(ILEmitterCtx context) + { + EmitCmp(context, OpCodes.Blt_S, scalar: false); + } + + public static void Cmtst_S(ILEmitterCtx context) + { + EmitCmtst(context, scalar: true); + } + + public static void Cmtst_V(ILEmitterCtx context) + { + EmitCmtst(context, scalar: false); + } + + public static void Fccmp_S(ILEmitterCtx context) + { + OpCodeSimdFcond64 op = (OpCodeSimdFcond64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.EmitCondBranch(lblTrue, op.Cond); + + context.EmitLdc_I4(op.Nzcv); + EmitSetNzcv(context); + + context.Emit(OpCodes.Br, lblEnd); + + context.MarkLabel(lblTrue); + + EmitFcmpE(context, signalNaNs: false); + + context.MarkLabel(lblEnd); + } + + public static void Fccmpe_S(ILEmitterCtx context) + { + OpCodeSimdFcond64 op = (OpCodeSimdFcond64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.EmitCondBranch(lblTrue, op.Cond); + + context.EmitLdc_I4(op.Nzcv); + EmitSetNzcv(context); + + context.Emit(OpCodes.Br, lblEnd); + + context.MarkLabel(lblTrue); + + EmitFcmpE(context, signalNaNs: true); + + context.MarkLabel(lblEnd); + } + + public static void Fcmeq_S(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareEqualScalar)); + } + else + { + EmitScalarFcmp(context, OpCodes.Beq_S); + } + } + + public static void Fcmeq_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareEqual)); + } + else + { + EmitVectorFcmp(context, OpCodes.Beq_S); + } + } + + public static void Fcmge_S(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar)); + } + else + { + EmitScalarFcmp(context, OpCodes.Bge_S); + } + } + + public static void Fcmge_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual)); + } + else + { + EmitVectorFcmp(context, OpCodes.Bge_S); + } + } + + public static void Fcmgt_S(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar)); + } + else + { + EmitScalarFcmp(context, OpCodes.Bgt_S); + } + } + + public static void Fcmgt_V(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse + && Optimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan)); + } + else + { + EmitVectorFcmp(context, OpCodes.Bgt_S); + } + } + + public static void Fcmle_S(ILEmitterCtx context) + { + EmitScalarFcmp(context, OpCodes.Ble_S); + } + + public static void Fcmle_V(ILEmitterCtx context) + { + EmitVectorFcmp(context, OpCodes.Ble_S); + } + + public static void Fcmlt_S(ILEmitterCtx context) + { + EmitScalarFcmp(context, OpCodes.Blt_S); + } + + public static void Fcmlt_V(ILEmitterCtx context) + { + EmitVectorFcmp(context, OpCodes.Blt_S); + } + + public static void Fcmp_S(ILEmitterCtx context) + { + EmitFcmpE(context, signalNaNs: false); + } + + public static void Fcmpe_S(ILEmitterCtx context) + { + EmitFcmpE(context, signalNaNs: true); + } + + private static void EmitFcmpE(ILEmitterCtx context, bool signalNaNs) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + bool cmpWithZero = !(op is OpCodeSimdFcond64) ? op.Bit3 : false; + + if (Optimizations.FastFP && Optimizations.UseSse2) + { + if (op.Size == 0) + { + Type[] typesCmp = new Type[] { typeof(Vector128), typeof(Vector128) }; + + ILLabel lblNaN = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.EmitLdvec(op.Rn); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + if (cmpWithZero) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + } + else + { + context.EmitLdvec(op.Rm); + } + + context.Emit(OpCodes.Dup); + context.EmitStvectmp2(); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareOrderedScalar), typesCmp)); + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareEqualOrderedScalar), typesCmp)); + + context.Emit(OpCodes.Brtrue_S, lblNaN); + + context.EmitLdc_I4(0); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareGreaterThanOrEqualOrderedScalar), typesCmp)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareEqualOrderedScalar), typesCmp)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareLessThanOrderedScalar), typesCmp)); + + context.EmitStflg((int)PState.NBit); + context.EmitStflg((int)PState.ZBit); + context.EmitStflg((int)PState.CBit); + context.EmitStflg((int)PState.VBit); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblNaN); + + context.EmitLdc_I4(1); + context.Emit(OpCodes.Dup); + context.EmitLdc_I4(0); + context.Emit(OpCodes.Dup); + + context.EmitStflg((int)PState.NBit); + context.EmitStflg((int)PState.ZBit); + context.EmitStflg((int)PState.CBit); + context.EmitStflg((int)PState.VBit); + + context.MarkLabel(lblEnd); + } + else /* if (op.Size == 1) */ + { + Type[] typesCmp = new Type[] { typeof(Vector128), typeof(Vector128) }; + + ILLabel lblNaN = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + EmitLdvecWithCastToDouble(context, op.Rn); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + if (cmpWithZero) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero)); + } + else + { + EmitLdvecWithCastToDouble(context, op.Rm); + } + + context.Emit(OpCodes.Dup); + context.EmitStvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareOrderedScalar), typesCmp)); + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqualOrderedScalar), typesCmp)); + + context.Emit(OpCodes.Brtrue_S, lblNaN); + + context.EmitLdc_I4(0); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThanOrEqualOrderedScalar), typesCmp)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqualOrderedScalar), typesCmp)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareLessThanOrderedScalar), typesCmp)); + + context.EmitStflg((int)PState.NBit); + context.EmitStflg((int)PState.ZBit); + context.EmitStflg((int)PState.CBit); + context.EmitStflg((int)PState.VBit); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblNaN); + + context.EmitLdc_I4(1); + context.Emit(OpCodes.Dup); + context.EmitLdc_I4(0); + context.Emit(OpCodes.Dup); + + context.EmitStflg((int)PState.NBit); + context.EmitStflg((int)PState.ZBit); + context.EmitStflg((int)PState.CBit); + context.EmitStflg((int)PState.VBit); + + context.MarkLabel(lblEnd); + } + } + else + { + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + if (cmpWithZero) + { + if (op.Size == 0) + { + context.EmitLdc_R4(0f); + } + else // if (op.Size == 1) + { + context.EmitLdc_R8(0d); + } + } + else + { + EmitVectorExtractF(context, op.Rm, 0, op.Size); + } + + context.EmitLdc_I4(!signalNaNs ? 0 : 1); + + EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare)); + + EmitSetNzcv(context); + } + } + + private static void EmitCmp(ILEmitterCtx context, OpCode ilOp, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size)); + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractSx(context, op.Rn, index, op.Size); + + if (op is OpCodeSimdReg64 binOp) + { + EmitVectorExtractSx(context, binOp.Rm, index, op.Size); + } + else + { + context.EmitLdc_I8(0L); + } + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(ilOp, lblTrue); + + EmitVectorInsert(context, op.Rd, index, op.Size, 0); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblTrue); + + EmitVectorInsert(context, op.Rd, index, op.Size, (long)szMask); + + context.MarkLabel(lblEnd); + } + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitCmtst(ILEmitterCtx context, bool scalar) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size)); + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + EmitVectorExtractZx(context, op.Rm, index, op.Size); + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(OpCodes.And); + + context.EmitLdc_I8(0L); + + context.Emit(OpCodes.Bne_Un_S, lblTrue); + + EmitVectorInsert(context, op.Rd, index, op.Size, 0); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblTrue); + + EmitVectorInsert(context, op.Rd, index, op.Size, (long)szMask); + + context.MarkLabel(lblEnd); + } + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitScalarFcmp(ILEmitterCtx context, OpCode ilOp) + { + EmitFcmp(context, ilOp, 0, scalar: true); + } + + private static void EmitVectorFcmp(ILEmitterCtx context, OpCode ilOp) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeF + 2; + + for (int index = 0; index < elems; index++) + { + EmitFcmp(context, ilOp, index, scalar: false); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitFcmp(ILEmitterCtx context, OpCode ilOp, int index, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + ulong szMask = ulong.MaxValue >> (64 - (32 << sizeF)); + + EmitVectorExtractF(context, op.Rn, index, sizeF); + + if (op is OpCodeSimdReg64 binOp) + { + EmitVectorExtractF(context, binOp.Rm, index, sizeF); + } + else if (sizeF == 0) + { + context.EmitLdc_R4(0f); + } + else /* if (sizeF == 1) */ + { + context.EmitLdc_R8(0d); + } + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.Emit(ilOp, lblTrue); + + if (scalar) + { + EmitVectorZeroAll(context, op.Rd); + } + else + { + EmitVectorInsert(context, op.Rd, index, sizeF + 2, 0); + } + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblTrue); + + if (scalar) + { + EmitVectorInsert(context, op.Rd, index, 3, (long)szMask); + + EmitVectorZeroUpper(context, op.Rd); + } + else + { + EmitVectorInsert(context, op.Rd, index, sizeF + 2, (long)szMask); + } + + context.MarkLabel(lblEnd); + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdCrypto.cs b/ChocolArm64/Instructions/InstEmitSimdCrypto.cs new file mode 100644 index 0000000000..33c81aab0a --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdCrypto.cs @@ -0,0 +1,54 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Aesd_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Decrypt)); + + context.EmitStvec(op.Rd); + } + + public static void Aese_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Encrypt)); + + context.EmitStvec(op.Rd); + } + + public static void Aesimc_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.InverseMixColumns)); + + context.EmitStvec(op.Rd); + } + + public static void Aesmc_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.MixColumns)); + + context.EmitStvec(op.Rd); + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdCvt.cs b/ChocolArm64/Instructions/InstEmitSimdCvt.cs new file mode 100644 index 0000000000..fe8722af3c --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdCvt.cs @@ -0,0 +1,757 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Fcvt_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + if (Optimizations.UseSse2) + { + if (op.Size == 1 && op.Opc == 0) + { + //Double -> Single. + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + EmitLdvecWithCastToDouble(context, op.Rn); + + Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), types)); + + context.EmitStvec(op.Rd); + } + else if (op.Size == 0 && op.Opc == 1) + { + //Single -> Double. + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero)); + + context.EmitLdvec(op.Rn); + + Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Double), types)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + else + { + //Invalid encoding. + throw new InvalidOperationException(); + } + } + else + { + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + EmitFloatCast(context, op.Opc); + + EmitScalarSetF(context, op.Rd, op.Opc); + } + } + + public static void Fcvtas_Gp(ILEmitterCtx context) + { + EmitFcvt_s_Gp(context, () => EmitRoundMathCall(context, MidpointRounding.AwayFromZero)); + } + + public static void Fcvtau_Gp(ILEmitterCtx context) + { + EmitFcvt_u_Gp(context, () => EmitRoundMathCall(context, MidpointRounding.AwayFromZero)); + } + + public static void Fcvtl_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (Optimizations.UseSse2 && sizeF == 1) + { + Type[] typesMov = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesCvt = new Type[] { typeof(Vector128) }; + + string nameMov = op.RegisterSize == RegisterSize.Simd128 + ? nameof(Sse.MoveHighToLow) + : nameof(Sse.MoveLowToHigh); + + context.EmitLdvec(op.Rn); + context.Emit(OpCodes.Dup); + + context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Double), typesCvt)); + + EmitStvecWithCastFromDouble(context, op.Rd); + } + else + { + int elems = 4 >> sizeF; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + for (int index = 0; index < elems; index++) + { + if (sizeF == 0) + { + EmitVectorExtractZx(context, op.Rn, part + index, 1); + context.Emit(OpCodes.Conv_U2); + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCall(typeof(SoftFloat16_32), nameof(SoftFloat16_32.FPConvert)); + } + else /* if (sizeF == 1) */ + { + EmitVectorExtractF(context, op.Rn, part + index, 0); + + context.Emit(OpCodes.Conv_R8); + } + + EmitVectorInsertTmpF(context, index, sizeF); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + } + } + + public static void Fcvtms_Gp(ILEmitterCtx context) + { + EmitFcvt_s_Gp(context, () => EmitUnaryMathCall(context, nameof(Math.Floor))); + } + + public static void Fcvtmu_Gp(ILEmitterCtx context) + { + EmitFcvt_u_Gp(context, () => EmitUnaryMathCall(context, nameof(Math.Floor))); + } + + public static void Fcvtn_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (Optimizations.UseSse2 && sizeF == 1) + { + Type[] typesMov = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesCvt = new Type[] { typeof(Vector128) }; + + string nameMov = op.RegisterSize == RegisterSize.Simd128 + ? nameof(Sse.MoveLowToHigh) + : nameof(Sse.MoveHighToLow); + + context.EmitLdvec(op.Rd); + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov)); + + EmitLdvecWithCastToDouble(context, op.Rn); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); + context.Emit(OpCodes.Dup); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov)); + + context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov)); + + context.EmitStvec(op.Rd); + } + else + { + int elems = 4 >> sizeF; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + + if (sizeF == 0) + { + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCall(typeof(SoftFloat32_16), nameof(SoftFloat32_16.FPConvert)); + + context.Emit(OpCodes.Conv_U8); + EmitVectorInsertTmp(context, part + index, 1); + } + else /* if (sizeF == 1) */ + { + context.Emit(OpCodes.Conv_R4); + + EmitVectorInsertTmpF(context, part + index, 0); + } + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + public static void Fcvtns_S(ILEmitterCtx context) + { + EmitFcvtn(context, signed: true, scalar: true); + } + + public static void Fcvtns_V(ILEmitterCtx context) + { + EmitFcvtn(context, signed: true, scalar: false); + } + + public static void Fcvtnu_S(ILEmitterCtx context) + { + EmitFcvtn(context, signed: false, scalar: true); + } + + public static void Fcvtnu_V(ILEmitterCtx context) + { + EmitFcvtn(context, signed: false, scalar: false); + } + + public static void Fcvtps_Gp(ILEmitterCtx context) + { + EmitFcvt_s_Gp(context, () => EmitUnaryMathCall(context, nameof(Math.Ceiling))); + } + + public static void Fcvtpu_Gp(ILEmitterCtx context) + { + EmitFcvt_u_Gp(context, () => EmitUnaryMathCall(context, nameof(Math.Ceiling))); + } + + public static void Fcvtzs_Gp(ILEmitterCtx context) + { + EmitFcvt_s_Gp(context, () => { }); + } + + public static void Fcvtzs_Gp_Fix(ILEmitterCtx context) + { + EmitFcvtzs_Gp_Fix(context); + } + + public static void Fcvtzs_S(ILEmitterCtx context) + { + EmitScalarFcvtzs(context); + } + + public static void Fcvtzs_V(ILEmitterCtx context) + { + EmitVectorFcvtzs(context); + } + + public static void Fcvtzu_Gp(ILEmitterCtx context) + { + EmitFcvt_u_Gp(context, () => { }); + } + + public static void Fcvtzu_Gp_Fix(ILEmitterCtx context) + { + EmitFcvtzu_Gp_Fix(context); + } + + public static void Fcvtzu_S(ILEmitterCtx context) + { + EmitScalarFcvtzu(context); + } + + public static void Fcvtzu_V(ILEmitterCtx context) + { + EmitVectorFcvtzu(context); + } + + public static void Scvtf_Gp(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U4); + } + + EmitFloatCast(context, op.Size); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Scvtf_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractSx(context, op.Rn, 0, op.Size + 2); + + EmitFloatCast(context, op.Size); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Scvtf_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (Optimizations.UseSse2 && sizeF == 0) + { + Type[] typesCvt = new Type[] { typeof(Vector128) }; + + EmitLdvecWithSignedCast(context, op.Rn, 2); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorCvtf(context, signed: true); + } + } + + public static void Ucvtf_Gp(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U4); + } + + context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(context, op.Size); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Ucvtf_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, 0, op.Size + 2); + + context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(context, op.Size); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Ucvtf_V(ILEmitterCtx context) + { + EmitVectorCvtf(context, signed: false); + } + + private static int GetFBits(ILEmitterCtx context) + { + if (context.CurrOp is OpCodeSimdShImm64 op) + { + return GetImmShr(op); + } + + return 0; + } + + private static void EmitFloatCast(ILEmitterCtx context, int size) + { + if (size == 0) + { + context.Emit(OpCodes.Conv_R4); + } + else if (size == 1) + { + context.Emit(OpCodes.Conv_R8); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + private static void EmitFcvtn(ILEmitterCtx context, bool signed, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> sizeI : 1; + + if (scalar && (sizeF == 0)) + { + EmitVectorZeroLowerTmp(context); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + + EmitRoundMathCall(context, MidpointRounding.ToEven); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF32ToS32) + : nameof(VectorHelper.SatF32ToU32)); + + context.Emit(OpCodes.Conv_U8); + } + else /* if (sizeF == 1) */ + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF64ToS64) + : nameof(VectorHelper.SatF64ToU64)); + } + + EmitVectorInsertTmp(context, index, sizeI); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitFcvt_s_Gp(ILEmitterCtx context, Action emit) + { + EmitFcvt___Gp(context, emit, true); + } + + private static void EmitFcvt_u_Gp(ILEmitterCtx context, Action emit) + { + EmitFcvt___Gp(context, emit, false); + } + + private static void EmitFcvt___Gp(ILEmitterCtx context, Action emit, bool signed) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + emit(); + + if (signed) + { + EmitScalarFcvts(context, op.Size, 0); + } + else + { + EmitScalarFcvtu(context, op.Size, 0); + } + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rd); + } + + private static void EmitFcvtzs_Gp_Fix(ILEmitterCtx context) + { + EmitFcvtz__Gp_Fix(context, true); + } + + private static void EmitFcvtzu_Gp_Fix(ILEmitterCtx context) + { + EmitFcvtz__Gp_Fix(context, false); + } + + private static void EmitFcvtz__Gp_Fix(ILEmitterCtx context, bool signed) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + if (signed) + { + EmitScalarFcvts(context, op.Size, op.FBits); + } + else + { + EmitScalarFcvtu(context, op.Size, op.FBits); + } + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rd); + } + + private static void EmitVectorCvtf(ILEmitterCtx context, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int fBits = GetFBits(context); + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeI; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, sizeI, signed); + + if (!signed) + { + context.Emit(OpCodes.Conv_R_Un); + } + + context.Emit(sizeF == 0 + ? OpCodes.Conv_R4 + : OpCodes.Conv_R8); + + EmitI2fFBitsMul(context, sizeF, fBits); + + EmitVectorInsertF(context, op.Rd, index, sizeF); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitScalarFcvtzs(ILEmitterCtx context) + { + EmitScalarFcvtz(context, true); + } + + private static void EmitScalarFcvtzu(ILEmitterCtx context) + { + EmitScalarFcvtz(context, false); + } + + private static void EmitScalarFcvtz(ILEmitterCtx context, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int fBits = GetFBits(context); + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + + EmitF2iFBitsMul(context, sizeF, fBits); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF32ToS32) + : nameof(VectorHelper.SatF32ToU32)); + } + else /* if (sizeF == 1) */ + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF64ToS64) + : nameof(VectorHelper.SatF64ToU64)); + } + + if (sizeF == 0) + { + context.Emit(OpCodes.Conv_U8); + } + + EmitScalarSet(context, op.Rd, sizeI); + } + + private static void EmitVectorFcvtzs(ILEmitterCtx context) + { + EmitVectorFcvtz(context, true); + } + + private static void EmitVectorFcvtzu(ILEmitterCtx context) + { + EmitVectorFcvtz(context, false); + } + + private static void EmitVectorFcvtz(ILEmitterCtx context, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int fBits = GetFBits(context); + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeI; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + + EmitF2iFBitsMul(context, sizeF, fBits); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF32ToS32) + : nameof(VectorHelper.SatF32ToU32)); + } + else /* if (sizeF == 1) */ + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF64ToS64) + : nameof(VectorHelper.SatF64ToU64)); + } + + if (sizeF == 0) + { + context.Emit(OpCodes.Conv_U8); + } + + EmitVectorInsert(context, op.Rd, index, sizeI); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitScalarFcvts(ILEmitterCtx context, int size, int fBits) + { + if (size < 0 || size > 1) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + EmitF2iFBitsMul(context, size, fBits); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF32ToS32)); + } + else /* if (size == 1) */ + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF64ToS32)); + } + } + else + { + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF32ToS64)); + } + else /* if (size == 1) */ + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF64ToS64)); + } + } + } + + private static void EmitScalarFcvtu(ILEmitterCtx context, int size, int fBits) + { + if (size < 0 || size > 1) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + EmitF2iFBitsMul(context, size, fBits); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF32ToU32)); + } + else /* if (size == 1) */ + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF64ToU32)); + } + } + else + { + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF32ToU64)); + } + else /* if (size == 1) */ + { + VectorHelper.EmitCall(context, nameof(VectorHelper.SatF64ToU64)); + } + } + } + + private static void EmitF2iFBitsMul(ILEmitterCtx context, int size, int fBits) + { + if (fBits != 0) + { + if (size == 0) + { + context.EmitLdc_R4(MathF.Pow(2f, fBits)); + } + else if (size == 1) + { + context.EmitLdc_R8(Math.Pow(2d, fBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.Emit(OpCodes.Mul); + } + } + + private static void EmitI2fFBitsMul(ILEmitterCtx context, int size, int fBits) + { + if (fBits != 0) + { + if (size == 0) + { + context.EmitLdc_R4(1f / MathF.Pow(2f, fBits)); + } + else if (size == 1) + { + context.EmitLdc_R8(1d / Math.Pow(2d, fBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.Emit(OpCodes.Mul); + } + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdHash.cs b/ChocolArm64/Instructions/InstEmitSimdHash.cs new file mode 100644 index 0000000000..bb767fec07 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdHash.cs @@ -0,0 +1,140 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Translation; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { +#region "Sha1" + public static void Sha1c_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + EmitVectorExtractZx(context, op.Rn, 0, 2); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashChoose)); + + context.EmitStvec(op.Rd); + } + + public static void Sha1h_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, 0, 2); + + SoftFallback.EmitCall(context, nameof(SoftFallback.FixedRotate)); + + EmitScalarSet(context, op.Rd, 2); + } + + public static void Sha1m_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + EmitVectorExtractZx(context, op.Rn, 0, 2); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashMajority)); + + context.EmitStvec(op.Rd); + } + + public static void Sha1p_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + EmitVectorExtractZx(context, op.Rn, 0, 2); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashParity)); + + context.EmitStvec(op.Rd); + } + + public static void Sha1su0_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Sha1SchedulePart1)); + + context.EmitStvec(op.Rd); + } + + public static void Sha1su1_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Sha1SchedulePart2)); + + context.EmitStvec(op.Rd); + } +#endregion + +#region "Sha256" + public static void Sha256h_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashLower)); + + context.EmitStvec(op.Rd); + } + + public static void Sha256h2_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.HashUpper)); + + context.EmitStvec(op.Rd); + } + + public static void Sha256su0_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Sha256SchedulePart1)); + + context.EmitStvec(op.Rd); + } + + public static void Sha256su1_V(ILEmitterCtx context) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + SoftFallback.EmitCall(context, nameof(SoftFallback.Sha256SchedulePart2)); + + context.EmitStvec(op.Rd); + } +#endregion + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdHelper.cs b/ChocolArm64/Instructions/InstEmitSimdHelper.cs new file mode 100644 index 0000000000..7b597be37d --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdHelper.cs @@ -0,0 +1,1513 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace ChocolArm64.Instructions +{ + static class InstEmitSimdHelper + { + public static readonly Type[] IntTypesPerSizeLog2 = new Type[] + { + typeof(sbyte), + typeof(short), + typeof(int), + typeof(long) + }; + + public static readonly Type[] UIntTypesPerSizeLog2 = new Type[] + { + typeof(byte), + typeof(ushort), + typeof(uint), + typeof(ulong) + }; + + public static readonly Type[] VectorIntTypesPerSizeLog2 = new Type[] + { + typeof(Vector128), + typeof(Vector128), + typeof(Vector128), + typeof(Vector128) + }; + + public static readonly Type[] VectorUIntTypesPerSizeLog2 = new Type[] + { + typeof(Vector128), + typeof(Vector128), + typeof(Vector128), + typeof(Vector128) + }; + + [Flags] + public enum OperFlags + { + Rd = 1 << 0, + Rn = 1 << 1, + Rm = 1 << 2, + Ra = 1 << 3, + + RnRm = Rn | Rm, + RdRn = Rd | Rn, + RaRnRm = Ra | Rn | Rm, + RdRnRm = Rd | Rn | Rm + } + + public static int GetImmShl(OpCodeSimdShImm64 op) + { + return op.Imm - (8 << op.Size); + } + + public static int GetImmShr(OpCodeSimdShImm64 op) + { + return (8 << (op.Size + 1)) - op.Imm; + } + + public static void EmitSse2Op(ILEmitterCtx context, string name) + { + EmitSseOp(context, name, typeof(Sse2)); + } + + public static void EmitSse41Op(ILEmitterCtx context, string name) + { + EmitSseOp(context, name, typeof(Sse41)); + } + + public static void EmitSse42Op(ILEmitterCtx context, string name) + { + EmitSseOp(context, name, typeof(Sse42)); + } + + private static void EmitSseOp(ILEmitterCtx context, string name, Type type) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + Type baseType = VectorIntTypesPerSizeLog2[op.Size]; + + if (op is OpCodeSimdReg64 binOp) + { + EmitLdvecWithSignedCast(context, binOp.Rm, op.Size); + + context.EmitCall(type.GetMethod(name, new Type[] { baseType, baseType })); + } + else + { + context.EmitCall(type.GetMethod(name, new Type[] { baseType })); + } + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitLdvecWithSignedCast(ILEmitterCtx context, int reg, int size) + { + context.EmitLdvec(reg); + + switch (size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToSByte)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt16)); break; + case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt32)); break; + case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt64)); break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + public static void EmitLdvecWithCastToDouble(ILEmitterCtx context, int reg) + { + context.EmitLdvec(reg); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToDouble)); + } + + public static void EmitStvecWithCastFromDouble(ILEmitterCtx context, int reg) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleToSingle)); + + context.EmitStvec(reg); + } + + public static void EmitLdvecWithUnsignedCast(ILEmitterCtx context, int reg, int size) + { + context.EmitLdvec(reg); + + switch (size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToByte)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt16)); break; + case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt32)); break; + case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt64)); break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + public static void EmitStvecWithSignedCast(ILEmitterCtx context, int reg, int size) + { + switch (size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSByteToSingle)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt16ToSingle)); break; + case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt32ToSingle)); break; + case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64ToSingle)); break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.EmitStvec(reg); + } + + public static void EmitStvecWithUnsignedCast(ILEmitterCtx context, int reg, int size) + { + switch (size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorByteToSingle)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt16ToSingle)); break; + case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt32ToSingle)); break; + case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt64ToSingle)); break; + + default: throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.EmitStvec(reg); + } + + public static void EmitScalarSseOrSse2OpF(ILEmitterCtx context, string name) + { + EmitSseOrSse2OpF(context, name, true); + } + + public static void EmitVectorSseOrSse2OpF(ILEmitterCtx context, string name) + { + EmitSseOrSse2OpF(context, name, false); + } + + public static void EmitSseOrSse2OpF(ILEmitterCtx context, string name, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + void Ldvec(int reg) + { + context.EmitLdvec(reg); + + if (sizeF == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToDouble)); + } + } + + Ldvec(op.Rn); + + Type type; + Type baseType; + + if (sizeF == 0) + { + type = typeof(Sse); + baseType = typeof(Vector128); + } + else /* if (sizeF == 1) */ + { + type = typeof(Sse2); + baseType = typeof(Vector128); + } + + if (op is OpCodeSimdReg64 binOp) + { + Ldvec(binOp.Rm); + + context.EmitCall(type.GetMethod(name, new Type[] { baseType, baseType })); + } + else + { + context.EmitCall(type.GetMethod(name, new Type[] { baseType })); + } + + if (sizeF == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleToSingle)); + } + + context.EmitStvec(op.Rd); + + if (scalar) + { + if (sizeF == 0) + { + EmitVectorZero32_128(context, op.Rd); + } + else /* if (sizeF == 1) */ + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitUnaryMathCall(ILEmitterCtx context, string name) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + MethodInfo mthdInfo; + + if (sizeF == 0) + { + mthdInfo = typeof(MathF).GetMethod(name, new Type[] { typeof(float) }); + } + else /* if (sizeF == 1) */ + { + mthdInfo = typeof(Math).GetMethod(name, new Type[] { typeof(double) }); + } + + context.EmitCall(mthdInfo); + } + + public static void EmitBinaryMathCall(ILEmitterCtx context, string name) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + MethodInfo mthdInfo; + + if (sizeF == 0) + { + mthdInfo = typeof(MathF).GetMethod(name, new Type[] { typeof(float), typeof(float) }); + } + else /* if (sizeF == 1) */ + { + mthdInfo = typeof(Math).GetMethod(name, new Type[] { typeof(double), typeof(double) }); + } + + context.EmitCall(mthdInfo); + } + + public static void EmitRoundMathCall(ILEmitterCtx context, MidpointRounding roundMode) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + MethodInfo mthdInfo; + + if (sizeF == 0) + { + mthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }); + } + else /* if (sizeF == 1) */ + { + mthdInfo = typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }); + } + + context.EmitLdc_I4((int)roundMode); + + context.EmitCall(mthdInfo); + } + + public static void EmitUnarySoftFloatCall(ILEmitterCtx context, string name) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + MethodInfo mthdInfo; + + if (sizeF == 0) + { + mthdInfo = typeof(SoftFloat).GetMethod(name, new Type[] { typeof(float) }); + } + else /* if (sizeF == 1) */ + { + mthdInfo = typeof(SoftFloat).GetMethod(name, new Type[] { typeof(double) }); + } + + context.EmitCall(mthdInfo); + } + + public static void EmitSoftFloatCall(ILEmitterCtx context, string name) + { + IOpCodeSimd64 op = (IOpCodeSimd64)context.CurrOp; + + Type type = (op.Size & 1) == 0 + ? typeof(SoftFloat32) + : typeof(SoftFloat64); + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + context.EmitCall(type, name); + } + + public static void EmitScalarBinaryOpByElemF(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + EmitScalarOpByElemF(context, emit, op.Index, ternary: false); + } + + public static void EmitScalarTernaryOpByElemF(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + EmitScalarOpByElemF(context, emit, op.Index, ternary: true); + } + + public static void EmitScalarOpByElemF(ILEmitterCtx context, Action emit, int elem, bool ternary) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (ternary) + { + EmitVectorExtractF(context, op.Rd, 0, sizeF); + } + + EmitVectorExtractF(context, op.Rn, 0, sizeF); + EmitVectorExtractF(context, op.Rm, elem, sizeF); + + emit(); + + EmitScalarSetF(context, op.Rd, sizeF); + } + + public static void EmitScalarUnaryOpSx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.Rn, true); + } + + public static void EmitScalarBinaryOpSx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.RnRm, true); + } + + public static void EmitScalarUnaryOpZx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.Rn, false); + } + + public static void EmitScalarBinaryOpZx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.RnRm, false); + } + + public static void EmitScalarTernaryOpZx(ILEmitterCtx context, Action emit) + { + EmitScalarOp(context, emit, OperFlags.RdRnRm, false); + } + + public static void EmitScalarOp(ILEmitterCtx context, Action emit, OperFlags opers, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + bool rd = (opers & OperFlags.Rd) != 0; + bool rn = (opers & OperFlags.Rn) != 0; + bool rm = (opers & OperFlags.Rm) != 0; + + if (rd) + { + EmitVectorExtract(context, op.Rd, 0, op.Size, signed); + } + + if (rn) + { + EmitVectorExtract(context, op.Rn, 0, op.Size, signed); + } + + if (rm) + { + EmitVectorExtract(context, ((OpCodeSimdReg64)op).Rm, 0, op.Size, signed); + } + + emit(); + + EmitScalarSet(context, op.Rd, op.Size); + } + + public static void EmitScalarUnaryOpF(ILEmitterCtx context, Action emit) + { + EmitScalarOpF(context, emit, OperFlags.Rn); + } + + public static void EmitScalarBinaryOpF(ILEmitterCtx context, Action emit) + { + EmitScalarOpF(context, emit, OperFlags.RnRm); + } + + public static void EmitScalarTernaryRaOpF(ILEmitterCtx context, Action emit) + { + EmitScalarOpF(context, emit, OperFlags.RaRnRm); + } + + public static void EmitScalarOpF(ILEmitterCtx context, Action emit, OperFlags opers) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + bool ra = (opers & OperFlags.Ra) != 0; + bool rn = (opers & OperFlags.Rn) != 0; + bool rm = (opers & OperFlags.Rm) != 0; + + if (ra) + { + EmitVectorExtractF(context, ((OpCodeSimdReg64)op).Ra, 0, sizeF); + } + + if (rn) + { + EmitVectorExtractF(context, op.Rn, 0, sizeF); + } + + if (rm) + { + EmitVectorExtractF(context, ((OpCodeSimdReg64)op).Rm, 0, sizeF); + } + + emit(); + + EmitScalarSetF(context, op.Rd, sizeF); + } + + public static void EmitVectorUnaryOpF(ILEmitterCtx context, Action emit) + { + EmitVectorOpF(context, emit, OperFlags.Rn); + } + + public static void EmitVectorBinaryOpF(ILEmitterCtx context, Action emit) + { + EmitVectorOpF(context, emit, OperFlags.RnRm); + } + + public static void EmitVectorTernaryOpF(ILEmitterCtx context, Action emit) + { + EmitVectorOpF(context, emit, OperFlags.RdRnRm); + } + + public static void EmitVectorOpF(ILEmitterCtx context, Action emit, OperFlags opers) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeF + 2; + + bool rd = (opers & OperFlags.Rd) != 0; + bool rn = (opers & OperFlags.Rn) != 0; + bool rm = (opers & OperFlags.Rm) != 0; + + for (int index = 0; index < elems; index++) + { + if (rd) + { + EmitVectorExtractF(context, op.Rd, index, sizeF); + } + + if (rn) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + } + + if (rm) + { + EmitVectorExtractF(context, ((OpCodeSimdReg64)op).Rm, index, sizeF); + } + + emit(); + + EmitVectorInsertF(context, op.Rd, index, sizeF); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorBinaryOpByElemF(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + EmitVectorOpByElemF(context, emit, op.Index, ternary: false); + } + + public static void EmitVectorTernaryOpByElemF(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElemF64 op = (OpCodeSimdRegElemF64)context.CurrOp; + + EmitVectorOpByElemF(context, emit, op.Index, ternary: true); + } + + public static void EmitVectorOpByElemF(ILEmitterCtx context, Action emit, int elem, bool ternary) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> sizeF + 2; + + for (int index = 0; index < elems; index++) + { + if (ternary) + { + EmitVectorExtractF(context, op.Rd, index, sizeF); + } + + EmitVectorExtractF(context, op.Rn, index, sizeF); + EmitVectorExtractF(context, op.Rm, elem, sizeF); + + emit(); + + EmitVectorInsertTmpF(context, index, sizeF); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorUnaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.Rn, true); + } + + public static void EmitVectorBinaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.RnRm, true); + } + + public static void EmitVectorTernaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.RdRnRm, true); + } + + public static void EmitVectorUnaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.Rn, false); + } + + public static void EmitVectorBinaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.RnRm, false); + } + + public static void EmitVectorTernaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorOp(context, emit, OperFlags.RdRnRm, false); + } + + public static void EmitVectorOp(ILEmitterCtx context, Action emit, OperFlags opers, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + bool rd = (opers & OperFlags.Rd) != 0; + bool rn = (opers & OperFlags.Rn) != 0; + bool rm = (opers & OperFlags.Rm) != 0; + + for (int index = 0; index < elems; index++) + { + if (rd) + { + EmitVectorExtract(context, op.Rd, index, op.Size, signed); + } + + if (rn) + { + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + } + + if (rm) + { + EmitVectorExtract(context, ((OpCodeSimdReg64)op).Rm, index, op.Size, signed); + } + + emit(); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorBinaryOpByElemSx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorOpByElem(context, emit, op.Index, false, true); + } + + public static void EmitVectorBinaryOpByElemZx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorOpByElem(context, emit, op.Index, false, false); + } + + public static void EmitVectorTernaryOpByElemZx(ILEmitterCtx context, Action emit) + { + OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp; + + EmitVectorOpByElem(context, emit, op.Index, true, false); + } + + public static void EmitVectorOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + EmitVectorExtract(context, op.Rm, elem, op.Size, signed); + context.EmitSttmp(); + + for (int index = 0; index < elems; index++) + { + if (ternary) + { + EmitVectorExtract(context, op.Rd, index, op.Size, signed); + } + + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + context.EmitLdtmp(); + + emit(); + + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorImmUnaryOp(ILEmitterCtx context, Action emit) + { + EmitVectorImmOp(context, emit, false); + } + + public static void EmitVectorImmBinaryOp(ILEmitterCtx context, Action emit) + { + EmitVectorImmOp(context, emit, true); + } + + public static void EmitVectorImmOp(ILEmitterCtx context, Action emit, bool binary) + { + OpCodeSimdImm64 op = (OpCodeSimdImm64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int index = 0; index < elems; index++) + { + if (binary) + { + EmitVectorExtractZx(context, op.Rd, index, op.Size); + } + + context.EmitLdc_I8(op.Imm); + + emit(); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorWidenRmBinaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRmBinaryOp(context, emit, true); + } + + public static void EmitVectorWidenRmBinaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRmBinaryOp(context, emit, false); + } + + public static void EmitVectorWidenRmBinaryOp(ILEmitterCtx context, Action emit, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size + 1, signed); + EmitVectorExtract(context, op.Rm, part + index, op.Size, signed); + + emit(); + + EmitVectorInsertTmp(context, index, op.Size + 1); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + } + + public static void EmitVectorWidenRnRmBinaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRnRmOp(context, emit, false, true); + } + + public static void EmitVectorWidenRnRmBinaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRnRmOp(context, emit, false, false); + } + + public static void EmitVectorWidenRnRmTernaryOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRnRmOp(context, emit, true, true); + } + + public static void EmitVectorWidenRnRmTernaryOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorWidenRnRmOp(context, emit, true, false); + } + + public static void EmitVectorWidenRnRmOp(ILEmitterCtx context, Action emit, bool ternary, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + for (int index = 0; index < elems; index++) + { + if (ternary) + { + EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed); + } + + EmitVectorExtract(context, op.Rn, part + index, op.Size, signed); + EmitVectorExtract(context, op.Rm, part + index, op.Size, signed); + + emit(); + + EmitVectorInsertTmp(context, index, op.Size + 1); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + } + + public static void EmitVectorPairwiseOpSx(ILEmitterCtx context, Action emit) + { + EmitVectorPairwiseOp(context, emit, true); + } + + public static void EmitVectorPairwiseOpZx(ILEmitterCtx context, Action emit) + { + EmitVectorPairwiseOp(context, emit, false); + } + + public static void EmitVectorPairwiseOp(ILEmitterCtx context, Action emit, bool signed) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtract(context, op.Rn, idx, op.Size, signed); + EmitVectorExtract(context, op.Rn, idx + 1, op.Size, signed); + + emit(); + + EmitVectorExtract(context, op.Rm, idx, op.Size, signed); + EmitVectorExtract(context, op.Rm, idx + 1, op.Size, signed); + + emit(); + + EmitVectorInsertTmp(context, pairs + index, op.Size); + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitVectorPairwiseOpF(ILEmitterCtx context, Action emit) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int sizeF = op.Size & 1; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> sizeF + 2; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractF(context, op.Rn, idx, sizeF); + EmitVectorExtractF(context, op.Rn, idx + 1, sizeF); + + emit(); + + EmitVectorExtractF(context, op.Rm, idx, sizeF); + EmitVectorExtractF(context, op.Rm, idx + 1, sizeF); + + emit(); + + EmitVectorInsertTmpF(context, pairs + index, sizeF); + EmitVectorInsertTmpF(context, index, sizeF); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + [Flags] + public enum SaturatingFlags + { + Scalar = 1 << 0, + Signed = 1 << 1, + + Add = 1 << 2, + Sub = 1 << 3, + + Accumulate = 1 << 4, + + ScalarSx = Scalar | Signed, + ScalarZx = Scalar, + + VectorSx = Signed, + VectorZx = 0 + } + + public static void EmitScalarSaturatingUnaryOpSx(ILEmitterCtx context, Action emit) + { + EmitSaturatingUnaryOpSx(context, emit, SaturatingFlags.ScalarSx); + } + + public static void EmitVectorSaturatingUnaryOpSx(ILEmitterCtx context, Action emit) + { + EmitSaturatingUnaryOpSx(context, emit, SaturatingFlags.VectorSx); + } + + public static void EmitSaturatingUnaryOpSx(ILEmitterCtx context, Action emit, SaturatingFlags flags) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + bool scalar = (flags & SaturatingFlags.Scalar) != 0; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + if (scalar) + { + EmitVectorZeroLowerTmp(context); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractSx(context, op.Rn, index, op.Size); + + emit(); + + if (op.Size <= 2) + { + EmitSatQ(context, op.Size, true, true); + } + else /* if (op.Size == 3) */ + { + EmitUnarySignedSatQAbsOrNeg(context); + } + + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void EmitScalarSaturatingBinaryOpSx(ILEmitterCtx context, SaturatingFlags flags) + { + EmitSaturatingBinaryOp(context, () => { }, SaturatingFlags.ScalarSx | flags); + } + + public static void EmitScalarSaturatingBinaryOpZx(ILEmitterCtx context, SaturatingFlags flags) + { + EmitSaturatingBinaryOp(context, () => { }, SaturatingFlags.ScalarZx | flags); + } + + public static void EmitVectorSaturatingBinaryOpSx(ILEmitterCtx context, SaturatingFlags flags) + { + EmitSaturatingBinaryOp(context, () => { }, SaturatingFlags.VectorSx | flags); + } + + public static void EmitVectorSaturatingBinaryOpZx(ILEmitterCtx context, SaturatingFlags flags) + { + EmitSaturatingBinaryOp(context, () => { }, SaturatingFlags.VectorZx | flags); + } + + public static void EmitSaturatingBinaryOp(ILEmitterCtx context, Action emit, SaturatingFlags flags) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + bool scalar = (flags & SaturatingFlags.Scalar) != 0; + bool signed = (flags & SaturatingFlags.Signed) != 0; + + bool add = (flags & SaturatingFlags.Add) != 0; + bool sub = (flags & SaturatingFlags.Sub) != 0; + + bool accumulate = (flags & SaturatingFlags.Accumulate) != 0; + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + if (scalar) + { + EmitVectorZeroLowerTmp(context); + } + + if (add || sub) + { + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + EmitVectorExtract(context, ((OpCodeSimdReg64)op).Rm, index, op.Size, signed); + + if (op.Size <= 2) + { + context.Emit(add ? OpCodes.Add : OpCodes.Sub); + + EmitSatQ(context, op.Size, true, signed); + } + else /* if (op.Size == 3) */ + { + if (add) + { + EmitBinarySatQAdd(context, signed); + } + else /* if (sub) */ + { + EmitBinarySatQSub(context, signed); + } + } + + EmitVectorInsertTmp(context, index, op.Size); + } + } + else if (accumulate) + { + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size, !signed); + EmitVectorExtract(context, op.Rd, index, op.Size, signed); + + if (op.Size <= 2) + { + context.Emit(OpCodes.Add); + + EmitSatQ(context, op.Size, true, signed); + } + else /* if (op.Size == 3) */ + { + EmitBinarySatQAccumulate(context, signed); + } + + EmitVectorInsertTmp(context, index, op.Size); + } + } + else + { + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + EmitVectorExtract(context, ((OpCodeSimdReg64)op).Rm, index, op.Size, signed); + + emit(); + + EmitSatQ(context, op.Size, true, signed); + + EmitVectorInsertTmp(context, index, op.Size); + } + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + [Flags] + public enum SaturatingNarrowFlags + { + Scalar = 1 << 0, + SignedSrc = 1 << 1, + SignedDst = 1 << 2, + + ScalarSxSx = Scalar | SignedSrc | SignedDst, + ScalarSxZx = Scalar | SignedSrc, + ScalarZxZx = Scalar, + + VectorSxSx = SignedSrc | SignedDst, + VectorSxZx = SignedSrc, + VectorZxZx = 0 + } + + public static void EmitSaturatingNarrowOp(ILEmitterCtx context, SaturatingNarrowFlags flags) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + bool scalar = (flags & SaturatingNarrowFlags.Scalar) != 0; + bool signedSrc = (flags & SaturatingNarrowFlags.SignedSrc) != 0; + bool signedDst = (flags & SaturatingNarrowFlags.SignedDst) != 0; + + int elems = !scalar ? 8 >> op.Size : 1; + + int part = !scalar && (op.RegisterSize == RegisterSize.Simd128) ? elems : 0; + + if (scalar) + { + EmitVectorZeroLowerTmp(context); + } + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size + 1, signedSrc); + + EmitSatQ(context, op.Size, signedSrc, signedDst); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + // TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned). + public static void EmitSatQ( + ILEmitterCtx context, + int sizeDst, + bool signedSrc, + bool signedDst) + { + if (sizeDst > 2) + { + throw new ArgumentOutOfRangeException(nameof(sizeDst)); + } + + context.EmitLdc_I4(sizeDst); + context.EmitLdarg(TranslatedSub.StateArgIdx); + + if (signedSrc) + { + SoftFallback.EmitCall(context, signedDst + ? nameof(SoftFallback.SignedSrcSignedDstSatQ) + : nameof(SoftFallback.SignedSrcUnsignedDstSatQ)); + } + else + { + SoftFallback.EmitCall(context, signedDst + ? nameof(SoftFallback.UnsignedSrcSignedDstSatQ) + : nameof(SoftFallback.UnsignedSrcUnsignedDstSatQ)); + } + } + + // TSrc (64bit) == TDst (64bit); signed. + public static void EmitUnarySignedSatQAbsOrNeg(ILEmitterCtx context) + { + if (((OpCodeSimd64)context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + SoftFallback.EmitCall(context, nameof(SoftFallback.UnarySignedSatQAbsOrNeg)); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQAdd(ILEmitterCtx context, bool signed) + { + if (((OpCodeSimdReg64)context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + SoftFallback.EmitCall(context, signed + ? nameof(SoftFallback.BinarySignedSatQAdd) + : nameof(SoftFallback.BinaryUnsignedSatQAdd)); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQSub(ILEmitterCtx context, bool signed) + { + if (((OpCodeSimdReg64)context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + SoftFallback.EmitCall(context, signed + ? nameof(SoftFallback.BinarySignedSatQSub) + : nameof(SoftFallback.BinaryUnsignedSatQSub)); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQAccumulate(ILEmitterCtx context, bool signed) + { + if (((OpCodeSimd64)context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + SoftFallback.EmitCall(context, signed + ? nameof(SoftFallback.BinarySignedSatQAcc) + : nameof(SoftFallback.BinaryUnsignedSatQAcc)); + } + + public static void EmitScalarSet(ILEmitterCtx context, int reg, int size) + { + EmitVectorZeroAll(context, reg); + EmitVectorInsert(context, reg, 0, size); + } + + public static void EmitScalarSetF(ILEmitterCtx context, int reg, int size) + { + if (Optimizations.UseSse41 && size == 0) + { + //If the type is float, we can perform insertion and + //zero the upper bits with a single instruction (INSERTPS); + context.EmitLdvec(reg); + + VectorHelper.EmitCall(context, nameof(VectorHelper.Sse41VectorInsertScalarSingle)); + + context.EmitStvec(reg); + } + else + { + EmitVectorZeroAll(context, reg); + EmitVectorInsertF(context, reg, 0, size); + } + } + + public static void EmitVectorExtractSx(ILEmitterCtx context, int reg, int index, int size) + { + EmitVectorExtract(context, reg, index, size, true); + } + + public static void EmitVectorExtractZx(ILEmitterCtx context, int reg, int index, int size) + { + EmitVectorExtract(context, reg, index, size, false); + } + + public static void EmitVectorExtract(ILEmitterCtx context, int reg, int index, int size, bool signed) + { + ThrowIfInvalid(index, size); + + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.VectorExtractIntSx) + : nameof(VectorHelper.VectorExtractIntZx)); + } + + public static void EmitVectorExtractF(ILEmitterCtx context, int reg, int index, int size) + { + ThrowIfInvalidF(index, size); + + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractSingle)); + } + else if (size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + public static void EmitVectorZeroAll(ILEmitterCtx context, int reg) + { + if (Optimizations.UseSse) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitStvec(reg); + } + else + { + EmitVectorZeroLower(context, reg); + EmitVectorZeroUpper(context, reg); + } + } + + public static void EmitVectorZeroLower(ILEmitterCtx context, int reg) + { + EmitVectorInsert(context, reg, 0, 3, 0); + } + + public static void EmitVectorZeroLowerTmp(ILEmitterCtx context) + { + if (Optimizations.UseSse) + { + context.EmitLdvectmp(); + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveHighToLow))); + + context.EmitStvectmp(); + } + else + { + EmitVectorInsertTmp(context, 0, 3, 0); + } + } + + public static void EmitVectorZeroUpper(ILEmitterCtx context, int reg) + { + if (Optimizations.UseSse) + { + //TODO: Use Sse2.MoveScalar once it is fixed, + //as of the time of writing it just crashes the JIT (SDK 2.1.500). + + /*Type[] typesMov = new Type[] { typeof(Vector128) }; + + EmitLdvecWithUnsignedCast(context, reg, 3); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MoveScalar), typesMov)); + + EmitStvecWithUnsignedCast(context, reg, 3);*/ + + context.EmitLdvec(reg); + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh))); + + context.EmitStvec(reg); + } + else + { + EmitVectorInsert(context, reg, 1, 3, 0); + } + } + + public static void EmitVectorZero32_128(ILEmitterCtx context, int reg) + { + if (!Sse.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + context.EmitLdvec(reg); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveScalar))); + + context.EmitStvec(reg); + } + + public static void EmitVectorInsert(ILEmitterCtx context, int reg, int index, int size) + { + ThrowIfInvalid(index, size); + + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertInt)); + + context.EmitStvec(reg); + } + + public static void EmitVectorInsertTmp(ILEmitterCtx context, int index, int size) + { + ThrowIfInvalid(index, size); + + context.EmitLdvectmp(); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertInt)); + + context.EmitStvectmp(); + } + + public static void EmitVectorInsert(ILEmitterCtx context, int reg, int index, int size, long value) + { + ThrowIfInvalid(index, size); + + context.EmitLdc_I8(value); + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertInt)); + + context.EmitStvec(reg); + } + + public static void EmitVectorInsertTmp(ILEmitterCtx context, int index, int size, long value) + { + ThrowIfInvalid(index, size); + + context.EmitLdc_I8(value); + context.EmitLdvectmp(); + context.EmitLdc_I4(index); + context.EmitLdc_I4(size); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertInt)); + + context.EmitStvectmp(); + } + + public static void EmitVectorInsertF(ILEmitterCtx context, int reg, int index, int size) + { + ThrowIfInvalidF(index, size); + + context.EmitLdvec(reg); + context.EmitLdc_I4(index); + + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertSingle)); + } + else if (size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.EmitStvec(reg); + } + + public static void EmitVectorInsertTmpF(ILEmitterCtx context, int index, int size) + { + ThrowIfInvalidF(index, size); + + context.EmitLdvectmp(); + context.EmitLdc_I4(index); + + if (size == 0) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertSingle)); + } + else if (size == 1) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + context.EmitStvectmp(); + } + + private static void ThrowIfInvalid(int index, int size) + { + if ((uint)size > 3u) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + if ((uint)index >= 16u >> size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + + private static void ThrowIfInvalidF(int index, int size) + { + if ((uint)size > 1u) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + if ((uint)index >= 4u >> size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdLogical.cs b/ChocolArm64/Instructions/InstEmitSimdLogical.cs new file mode 100644 index 0000000000..f51568ebbe --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdLogical.cs @@ -0,0 +1,311 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void And_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.And)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.And)); + } + } + + public static void Bic_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + Type[] types = new Type[] + { + VectorUIntTypesPerSizeLog2[op.Size], + VectorUIntTypesPerSizeLog2[op.Size] + }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), types)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Not); + context.Emit(OpCodes.And); + }); + } + } + + public static void Bic_Vi(ILEmitterCtx context) + { + EmitVectorImmBinaryOp(context, () => + { + context.Emit(OpCodes.Not); + context.Emit(OpCodes.And); + }); + } + + public static void Bif_V(ILEmitterCtx context) + { + EmitBitBif(context, true); + } + + public static void Bit_V(ILEmitterCtx context) + { + EmitBitBif(context, false); + } + + private static void EmitBitBif(ILEmitterCtx context, bool notRm) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2) + { + Type[] types = new Type[] + { + VectorUIntTypesPerSizeLog2[op.Size], + VectorUIntTypesPerSizeLog2[op.Size] + }; + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + string name = notRm ? nameof(Sse2.AndNot) : nameof(Sse2.And); + + context.EmitCall(typeof(Sse2).GetMethod(name, types)); + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rd, index, op.Size); + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.Emit(OpCodes.Xor); + + EmitVectorExtractZx(context, op.Rm, index, op.Size); + + if (notRm) + { + context.Emit(OpCodes.Not); + } + + context.Emit(OpCodes.And); + + EmitVectorExtractZx(context, op.Rd, index, op.Size); + + context.Emit(OpCodes.Xor); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + public static void Bsl_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] types = new Type[] + { + VectorUIntTypesPerSizeLog2[op.Size], + VectorUIntTypesPerSizeLog2[op.Size] + }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), types)); + + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorTernaryOpZx(context, () => + { + context.EmitSttmp(); + context.EmitLdtmp(); + + context.Emit(OpCodes.Xor); + context.Emit(OpCodes.And); + + context.EmitLdtmp(); + + context.Emit(OpCodes.Xor); + }); + } + } + + public static void Eor_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.Xor)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Xor)); + } + } + + public static void Not_V(ILEmitterCtx context) + { + EmitVectorUnaryOpZx(context, () => context.Emit(OpCodes.Not)); + } + + public static void Orn_V(ILEmitterCtx context) + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Not); + context.Emit(OpCodes.Or); + }); + } + + public static void Orr_V(ILEmitterCtx context) + { + if (Optimizations.UseSse2) + { + EmitSse2Op(context, nameof(Sse2.Or)); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Or)); + } + } + + public static void Orr_Vi(ILEmitterCtx context) + { + EmitVectorImmBinaryOp(context, () => context.Emit(OpCodes.Or)); + } + + public static void Rbit_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, 0); + + context.Emit(OpCodes.Conv_U4); + + SoftFallback.EmitCall(context, nameof(SoftFallback.ReverseBits8)); + + context.Emit(OpCodes.Conv_U8); + + EmitVectorInsert(context, op.Rd, index, 0); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Rev16_V(ILEmitterCtx context) + { + EmitRev_V(context, containerSize: 1); + } + + public static void Rev32_V(ILEmitterCtx context) + { + EmitRev_V(context, containerSize: 2); + } + + public static void Rev64_V(ILEmitterCtx context) + { + EmitRev_V(context, containerSize: 3); + } + + private static void EmitRev_V(ILEmitterCtx context, int containerSize) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + if (op.Size >= containerSize) + { + throw new InvalidOperationException(); + } + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + int containerMask = (1 << (containerSize - op.Size)) - 1; + + for (int index = 0; index < elems; index++) + { + int revIndex = index ^ containerMask; + + EmitVectorExtractZx(context, op.Rn, revIndex, op.Size); + + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdMemory.cs b/ChocolArm64/Instructions/InstEmitSimdMemory.cs new file mode 100644 index 0000000000..eb05325786 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdMemory.cs @@ -0,0 +1,185 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instructions.InstEmitMemoryHelper; +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Ld__Vms(ILEmitterCtx context) + { + EmitSimdMemMs(context, isLoad: true); + } + + public static void Ld__Vss(ILEmitterCtx context) + { + EmitSimdMemSs(context, isLoad: true); + } + + public static void St__Vms(ILEmitterCtx context) + { + EmitSimdMemMs(context, isLoad: false); + } + + public static void St__Vss(ILEmitterCtx context) + { + EmitSimdMemSs(context, isLoad: false); + } + + private static void EmitSimdMemMs(ILEmitterCtx context, bool isLoad) + { + OpCodeSimdMemMs64 op = (OpCodeSimdMemMs64)context.CurrOp; + + int offset = 0; + + for (int rep = 0; rep < op.Reps; rep++) + for (int elem = 0; elem < op.Elems; elem++) + for (int sElem = 0; sElem < op.SElems; sElem++) + { + int rtt = (op.Rt + rep + sElem) & 0x1f; + + if (isLoad) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(op.Rn); + context.EmitLdc_I8(offset); + + context.Emit(OpCodes.Add); + + EmitReadZxCall(context, op.Size); + + EmitVectorInsert(context, rtt, elem, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64 && elem == op.Elems - 1) + { + EmitVectorZeroUpper(context, rtt); + } + } + else + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(op.Rn); + context.EmitLdc_I8(offset); + + context.Emit(OpCodes.Add); + + EmitVectorExtractZx(context, rtt, elem, op.Size); + + EmitWriteCall(context, op.Size); + } + + offset += 1 << op.Size; + } + + if (op.WBack) + { + EmitSimdMemWBack(context, offset); + } + } + + private static void EmitSimdMemSs(ILEmitterCtx context, bool isLoad) + { + OpCodeSimdMemSs64 op = (OpCodeSimdMemSs64)context.CurrOp; + + int offset = 0; + + void EmitMemAddress() + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdint(op.Rn); + context.EmitLdc_I8(offset); + + context.Emit(OpCodes.Add); + } + + if (op.Replicate) + { + //Only loads uses the replicate mode. + if (!isLoad) + { + throw new InvalidOperationException(); + } + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int sElem = 0; sElem < op.SElems; sElem++) + { + int rt = (op.Rt + sElem) & 0x1f; + + for (int index = 0; index < elems; index++) + { + EmitMemAddress(); + + EmitReadZxCall(context, op.Size); + + EmitVectorInsert(context, rt, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, rt); + } + + offset += 1 << op.Size; + } + } + else + { + for (int sElem = 0; sElem < op.SElems; sElem++) + { + int rt = (op.Rt + sElem) & 0x1f; + + if (isLoad) + { + EmitMemAddress(); + + EmitReadZxCall(context, op.Size); + + EmitVectorInsert(context, rt, op.Index, op.Size); + } + else + { + EmitMemAddress(); + + EmitVectorExtractZx(context, rt, op.Index, op.Size); + + EmitWriteCall(context, op.Size); + } + + offset += 1 << op.Size; + } + } + + if (op.WBack) + { + EmitSimdMemWBack(context, offset); + } + } + + private static void EmitSimdMemWBack(ILEmitterCtx context, int offset) + { + OpCodeMemReg64 op = (OpCodeMemReg64)context.CurrOp; + + context.EmitLdint(op.Rn); + + if (op.Rm != CpuThreadState.ZrIndex) + { + context.EmitLdint(op.Rm); + } + else + { + context.EmitLdc_I8(offset); + } + + context.Emit(OpCodes.Add); + + context.EmitStint(op.Rn); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitSimdMove.cs b/ChocolArm64/Instructions/InstEmitSimdMove.cs new file mode 100644 index 0000000000..0d9aa31212 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdMove.cs @@ -0,0 +1,611 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Dup_Gp(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + if (Optimizations.UseSse2) + { + Type[] typesSav = new Type[] { UIntTypesPerSizeLog2[op.Size] }; + + context.EmitLdintzr(op.Rn); + + switch (op.Size) + { + case 0: context.Emit(OpCodes.Conv_U1); break; + case 1: context.Emit(OpCodes.Conv_U2); break; + case 2: context.Emit(OpCodes.Conv_U4); break; + } + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + } + else + { + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int index = 0; index < elems; index++) + { + context.EmitLdintzr(op.Rn); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Dup_S(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size); + + EmitScalarSet(context, op.Rd, op.Size); + } + + public static void Dup_V(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + if (Optimizations.UseSse2) + { + Type[] typesSav = new Type[] { UIntTypesPerSizeLog2[op.Size] }; + + EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size); + + switch (op.Size) + { + case 0: context.Emit(OpCodes.Conv_U1); break; + case 1: context.Emit(OpCodes.Conv_U2); break; + case 2: context.Emit(OpCodes.Conv_U4); break; + } + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + } + else + { + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Ext_V(ILEmitterCtx context) + { + OpCodeSimdExt64 op = (OpCodeSimdExt64)context.CurrOp; + + if (Optimizations.UseSse2) + { + Type[] typesShs = new Type[] { typeof(Vector128), typeof(byte) }; + Type[] typesOr = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithUnsignedCast(context, op.Rn, 0); + + if (op.RegisterSize == RegisterSize.Simd64) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh))); + } + + context.EmitLdc_I4(op.Imm4); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesShs)); + + EmitLdvecWithUnsignedCast(context, op.Rm, 0); + + context.EmitLdc_I4((op.RegisterSize == RegisterSize.Simd64 ? 8 : 16) - op.Imm4); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical128BitLane), typesShs)); + + if (op.RegisterSize == RegisterSize.Simd64) + { + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh))); + } + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesOr)); + + EmitStvecWithUnsignedCast(context, op.Rd, 0); + } + else + { + int bytes = op.GetBitsCount() >> 3; + + int position = op.Imm4; + + for (int index = 0; index < bytes; index++) + { + int reg = op.Imm4 + index < bytes ? op.Rn : op.Rm; + + if (position == bytes) + { + position = 0; + } + + EmitVectorExtractZx(context, reg, position++, 0); + EmitVectorInsertTmp(context, index, 0); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + public static void Fcsel_S(ILEmitterCtx context) + { + OpCodeSimdFcond64 op = (OpCodeSimdFcond64)context.CurrOp; + + ILLabel lblTrue = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + context.EmitCondBranch(lblTrue, op.Cond); + + EmitVectorExtractF(context, op.Rm, 0, op.Size); + + context.Emit(OpCodes.Br_S, lblEnd); + + context.MarkLabel(lblTrue); + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + context.MarkLabel(lblEnd); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Fmov_Ftoi(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, 0, 3); + + EmitIntZeroUpperIfNeeded(context); + + context.EmitStintzr(op.Rd); + } + + public static void Fmov_Ftoi1(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, 1, 3); + + EmitIntZeroUpperIfNeeded(context); + + context.EmitStintzr(op.Rd); + } + + public static void Fmov_Itof(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + EmitIntZeroUpperIfNeeded(context); + + EmitScalarSet(context, op.Rd, 3); + } + + public static void Fmov_Itof1(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + EmitIntZeroUpperIfNeeded(context); + + EmitVectorInsert(context, op.Rd, 1, 3); + } + + public static void Fmov_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + EmitVectorExtractF(context, op.Rn, 0, op.Size); + + EmitScalarSetF(context, op.Rd, op.Size); + } + + public static void Fmov_Si(ILEmitterCtx context) + { + OpCodeSimdFmov64 op = (OpCodeSimdFmov64)context.CurrOp; + + context.EmitLdc_I8(op.Imm); + + EmitScalarSet(context, op.Rd, op.Size + 2); + } + + public static void Fmov_V(ILEmitterCtx context) + { + OpCodeSimdImm64 op = (OpCodeSimdImm64)context.CurrOp; + + int elems = op.RegisterSize == RegisterSize.Simd128 ? 4 : 2; + + for (int index = 0; index < (elems >> op.Size); index++) + { + context.EmitLdc_I8(op.Imm); + + EmitVectorInsert(context, op.Rd, index, op.Size + 2); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Ins_Gp(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + EmitVectorInsert(context, op.Rd, op.DstIndex, op.Size); + } + + public static void Ins_V(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, op.SrcIndex, op.Size); + + EmitVectorInsert(context, op.Rd, op.DstIndex, op.Size); + } + + public static void Movi_V(ILEmitterCtx context) + { + EmitVectorImmUnaryOp(context, () => { }); + } + + public static void Mvni_V(ILEmitterCtx context) + { + EmitVectorImmUnaryOp(context, () => context.Emit(OpCodes.Not)); + } + + public static void Smov_S(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + EmitVectorExtractSx(context, op.Rn, op.DstIndex, op.Size); + + EmitIntZeroUpperIfNeeded(context); + + context.EmitStintzr(op.Rd); + } + + public static void Tbl_V(ILEmitterCtx context) + { + OpCodeSimdTbl64 op = (OpCodeSimdTbl64)context.CurrOp; + + context.EmitLdvec(op.Rm); + + for (int index = 0; index < op.Size; index++) + { + context.EmitLdvec((op.Rn + index) & 0x1f); + } + + switch (op.Size) + { + case 1: VectorHelper.EmitCall(context, + nameof(VectorHelper.Tbl1_V64), + nameof(VectorHelper.Tbl1_V128)); break; + + case 2: VectorHelper.EmitCall(context, + nameof(VectorHelper.Tbl2_V64), + nameof(VectorHelper.Tbl2_V128)); break; + + case 3: VectorHelper.EmitCall(context, + nameof(VectorHelper.Tbl3_V64), + nameof(VectorHelper.Tbl3_V128)); break; + + case 4: VectorHelper.EmitCall(context, + nameof(VectorHelper.Tbl4_V64), + nameof(VectorHelper.Tbl4_V128)); break; + + default: throw new InvalidOperationException(); + } + + context.EmitStvec(op.Rd); + } + + public static void Trn1_V(ILEmitterCtx context) + { + EmitVectorTranspose(context, part: 0); + } + + public static void Trn2_V(ILEmitterCtx context) + { + EmitVectorTranspose(context, part: 1); + } + + public static void Umov_S(ILEmitterCtx context) + { + OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp; + + EmitVectorExtractZx(context, op.Rn, op.DstIndex, op.Size); + + context.EmitStintzr(op.Rd); + } + + public static void Uzp1_V(ILEmitterCtx context) + { + EmitVectorUnzip(context, part: 0); + } + + public static void Uzp2_V(ILEmitterCtx context) + { + EmitVectorUnzip(context, part: 1); + } + + public static void Xtn_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + if (Optimizations.UseSse41 && op.Size < 2) + { + void EmitZeroVector() + { + switch (op.Size) + { + case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt16Zero)); break; + case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt32Zero)); break; + } + } + + //For XTN, first operand is source, second operand is 0. + //For XTN2, first operand is 0, second operand is source. + if (part != 0) + { + EmitZeroVector(); + } + + EmitLdvecWithSignedCast(context, op.Rn, op.Size + 1); + + //Set mask to discard the upper half of the wide elements. + switch (op.Size) + { + case 0: context.EmitLdc_I4(0x00ff); break; + case 1: context.EmitLdc_I4(0x0000ffff); break; + } + + Type wideType = IntTypesPerSizeLog2[op.Size + 1]; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), new Type[] { wideType })); + + wideType = VectorIntTypesPerSizeLog2[op.Size + 1]; + + Type[] wideTypes = new Type[] { wideType, wideType }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), wideTypes)); + + if (part == 0) + { + EmitZeroVector(); + } + + //Pack values with signed saturation, the signed saturation shouldn't + //saturate anything since the upper bits were masked off. + Type sseType = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + context.EmitCall(sseType.GetMethod(nameof(Sse2.PackUnsignedSaturate), wideTypes)); + + if (part != 0) + { + //For XTN2, we additionally need to discard the upper bits + //of the target register and OR the result with it. + EmitVectorZeroUpper(context, op.Rd); + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + + Type narrowType = VectorUIntTypesPerSizeLog2[op.Size]; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), new Type[] { narrowType, narrowType })); + } + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + } + else + { + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size + 1); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + public static void Zip1_V(ILEmitterCtx context) + { + EmitVectorZip(context, part: 0); + } + + public static void Zip2_V(ILEmitterCtx context) + { + EmitVectorZip(context, part: 1); + } + + private static void EmitIntZeroUpperIfNeeded(ILEmitterCtx context) + { + if (context.CurrOp.RegisterSize == RegisterSize.Int32 || + context.CurrOp.RegisterSize == RegisterSize.Simd64) + { + context.Emit(OpCodes.Conv_U4); + context.Emit(OpCodes.Conv_U8); + } + } + + private static void EmitVectorTranspose(ILEmitterCtx context, int part) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); + EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); + + EmitVectorInsertTmp(context, idx + 1, op.Size); + EmitVectorInsertTmp(context, idx, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitVectorUnzip(ILEmitterCtx context, int part) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractZx(context, op.Rn, idx + part, op.Size); + EmitVectorExtractZx(context, op.Rm, idx + part, op.Size); + + EmitVectorInsertTmp(context, pairs + index, op.Size); + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitVectorZip(ILEmitterCtx context, int part) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse2) + { + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + + Type[] types = new Type[] + { + VectorUIntTypesPerSizeLog2[op.Size], + VectorUIntTypesPerSizeLog2[op.Size] + }; + + string name = part == 0 || (part != 0 && op.RegisterSize == RegisterSize.Simd64) + ? nameof(Sse2.UnpackLow) + : nameof(Sse2.UnpackHigh); + + context.EmitCall(typeof(Sse2).GetMethod(name, types)); + + if (op.RegisterSize == RegisterSize.Simd64 && part != 0) + { + context.EmitLdc_I4(8); + + Type[] shTypes = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), shTypes)); + } + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64 && part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + int words = op.GetBitsCount() >> 4; + int pairs = words >> op.Size; + + int Base = part != 0 ? pairs : 0; + + for (int index = 0; index < pairs; index++) + { + int idx = index << 1; + + EmitVectorExtractZx(context, op.Rn, Base + index, op.Size); + EmitVectorExtractZx(context, op.Rm, Base + index, op.Size); + + EmitVectorInsertTmp(context, idx + 1, op.Size); + EmitVectorInsertTmp(context, idx, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSimdShift.cs b/ChocolArm64/Instructions/InstEmitSimdShift.cs new file mode 100644 index 0000000000..b183e8aa66 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSimdShift.cs @@ -0,0 +1,865 @@ +// https://github.com/intel/ARM_NEON_2_x86_SSE/blob/master/NEON_2_SSE.h + +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; + +using static ChocolArm64.Instructions.InstEmitSimdHelper; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Rshrn_V(ILEmitterCtx context) + { + EmitVectorShrImmNarrowOpZx(context, round: true); + } + + public static void Shl_S(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + EmitScalarUnaryOpZx(context, () => + { + context.EmitLdc_I4(GetImmShl(op)); + + context.Emit(OpCodes.Shl); + }); + } + + public static void Shl_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSll = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShl(op)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSll)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorUnaryOpZx(context, () => + { + context.EmitLdc_I4(GetImmShl(op)); + + context.Emit(OpCodes.Shl); + }); + } + } + + public static void Shll_V(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int shift = 8 << op.Size; + + EmitVectorShImmWidenBinaryZx(context, () => context.Emit(OpCodes.Shl), shift); + } + + public static void Shrn_V(ILEmitterCtx context) + { + EmitVectorShrImmNarrowOpZx(context, round: false); + } + + public static void Sli_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + int bytes = op.GetBitsCount() >> 3; + int elems = bytes >> op.Size; + + int shift = GetImmShl(op); + + ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size); + + context.EmitLdc_I4(shift); + + context.Emit(OpCodes.Shl); + + EmitVectorExtractZx(context, op.Rd, index, op.Size); + + context.EmitLdc_I8((long)mask); + + context.Emit(OpCodes.And); + context.Emit(OpCodes.Or); + + EmitVectorInsert(context, op.Rd, index, op.Size); + } + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + public static void Sqrshrn_S(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxSx); + } + + public static void Sqrshrn_V(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxSx); + } + + public static void Sqrshrun_S(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxZx); + } + + public static void Sqrshrun_V(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx); + } + + public static void Sqshrn_S(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxSx); + } + + public static void Sqshrn_V(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxSx); + } + + public static void Sqshrun_S(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarSxZx); + } + + public static void Sqshrun_V(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx); + } + + public static void Srshr_S(ILEmitterCtx context) + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Round); + } + + public static void Srshr_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0 + && op.Size < 3) + { + Type[] typesShs = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + int shift = GetImmShr(op); + int eSize = 8 << op.Size; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + context.EmitLdc_I4(eSize - shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdc_I4(eSize - 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpSx(context, ShrImmFlags.Round); + } + } + + public static void Srsra_S(ILEmitterCtx context) + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } + + public static void Srsra_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0 + && op.Size < 3) + { + Type[] typesShs = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + int shift = GetImmShr(op); + int eSize = 8 << op.Size; + + EmitLdvecWithSignedCast(context, op.Rd, op.Size); + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + context.EmitLdc_I4(eSize - shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdc_I4(eSize - 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpSx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } + } + + public static void Sshl_V(ILEmitterCtx context) + { + EmitVectorShl(context, signed: true); + } + + public static void Sshll_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + EmitVectorShImmWidenBinarySx(context, () => context.Emit(OpCodes.Shl), GetImmShl(op)); + } + + public static void Sshr_S(ILEmitterCtx context) + { + EmitShrImmOp(context, ShrImmFlags.ScalarSx); + } + + public static void Sshr_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0 + && op.Size < 3) + { + Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShr(op)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitShrImmOp(context, ShrImmFlags.VectorSx); + } + } + + public static void Ssra_S(ILEmitterCtx context) + { + EmitScalarShrImmOpSx(context, ShrImmFlags.Accumulate); + } + + public static void Ssra_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0 + && op.Size < 3) + { + Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithSignedCast(context, op.Rd, op.Size); + EmitLdvecWithSignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShr(op)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithSignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpSx(context, ShrImmFlags.Accumulate); + } + } + + public static void Uqrshrn_S(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarZxZx); + } + + public static void Uqrshrn_V(ILEmitterCtx context) + { + EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorZxZx); + } + + public static void Uqshrn_S(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.ScalarZxZx); + } + + public static void Uqshrn_V(ILEmitterCtx context) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorZxZx); + } + + public static void Urshr_S(ILEmitterCtx context) + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Round); + } + + public static void Urshr_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesShs = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + int shift = GetImmShr(op); + int eSize = 8 << op.Size; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + context.EmitLdc_I4(eSize - shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdc_I4(eSize - 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpZx(context, ShrImmFlags.Round); + } + } + + public static void Ursra_S(ILEmitterCtx context) + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } + + public static void Ursra_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesShs = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + int shift = GetImmShr(op); + int eSize = 8 << op.Size; + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.Emit(OpCodes.Dup); + context.EmitStvectmp(); + + context.EmitLdc_I4(eSize - shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdc_I4(eSize - 1); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(shift); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpZx(context, ShrImmFlags.Round | ShrImmFlags.Accumulate); + } + } + + public static void Ushl_V(ILEmitterCtx context) + { + EmitVectorShl(context, signed: false); + } + + public static void Ushll_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + EmitVectorShImmWidenBinaryZx(context, () => context.Emit(OpCodes.Shl), GetImmShl(op)); + } + + public static void Ushr_S(ILEmitterCtx context) + { + EmitShrImmOp(context, ShrImmFlags.ScalarZx); + } + + public static void Ushr_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShr(op)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitShrImmOp(context, ShrImmFlags.VectorZx); + } + } + + public static void Usra_S(ILEmitterCtx context) + { + EmitScalarShrImmOpZx(context, ShrImmFlags.Accumulate); + } + + public static void Usra_V(ILEmitterCtx context) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + if (Optimizations.UseSse2 && op.Size > 0) + { + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + + EmitLdvecWithUnsignedCast(context, op.Rd, op.Size); + EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); + + context.EmitLdc_I4(GetImmShr(op)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorShrImmOpZx(context, ShrImmFlags.Accumulate); + } + } + + private static void EmitVectorShl(ILEmitterCtx context, bool signed) + { + //This instruction shifts the value on vector A by the number of bits + //specified on the signed, lower 8 bits of vector B. If the shift value + //is greater or equal to the data size of each lane, then the result is zero. + //Additionally, negative shifts produces right shifts by the negated shift value. + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int maxShift = 8 << op.Size; + + Action emit = () => + { + ILLabel lblShl = new ILLabel(); + ILLabel lblZero = new ILLabel(); + ILLabel lblEnd = new ILLabel(); + + void EmitShift(OpCode ilOp) + { + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(maxShift); + + context.Emit(OpCodes.Bge_S, lblZero); + context.Emit(ilOp); + context.Emit(OpCodes.Br_S, lblEnd); + } + + context.Emit(OpCodes.Conv_I1); + context.Emit(OpCodes.Dup); + + context.EmitLdc_I4(0); + + context.Emit(OpCodes.Bge_S, lblShl); + context.Emit(OpCodes.Neg); + + EmitShift(signed + ? OpCodes.Shr + : OpCodes.Shr_Un); + + context.MarkLabel(lblShl); + + EmitShift(OpCodes.Shl); + + context.MarkLabel(lblZero); + + context.Emit(OpCodes.Pop); + context.Emit(OpCodes.Pop); + + context.EmitLdc_I8(0); + + context.MarkLabel(lblEnd); + }; + + if (signed) + { + EmitVectorBinaryOpSx(context, emit); + } + else + { + EmitVectorBinaryOpZx(context, emit); + } + } + + [Flags] + private enum ShrImmFlags + { + Scalar = 1 << 0, + Signed = 1 << 1, + + Round = 1 << 2, + Accumulate = 1 << 3, + + ScalarSx = Scalar | Signed, + ScalarZx = Scalar, + + VectorSx = Signed, + VectorZx = 0 + } + + private static void EmitScalarShrImmOpSx(ILEmitterCtx context, ShrImmFlags flags) + { + EmitShrImmOp(context, ShrImmFlags.ScalarSx | flags); + } + + private static void EmitScalarShrImmOpZx(ILEmitterCtx context, ShrImmFlags flags) + { + EmitShrImmOp(context, ShrImmFlags.ScalarZx | flags); + } + + private static void EmitVectorShrImmOpSx(ILEmitterCtx context, ShrImmFlags flags) + { + EmitShrImmOp(context, ShrImmFlags.VectorSx | flags); + } + + private static void EmitVectorShrImmOpZx(ILEmitterCtx context, ShrImmFlags flags) + { + EmitShrImmOp(context, ShrImmFlags.VectorZx | flags); + } + + private static void EmitShrImmOp(ILEmitterCtx context, ShrImmFlags flags) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + bool scalar = (flags & ShrImmFlags.Scalar) != 0; + bool signed = (flags & ShrImmFlags.Signed) != 0; + bool round = (flags & ShrImmFlags.Round) != 0; + bool accumulate = (flags & ShrImmFlags.Accumulate) != 0; + + int shift = GetImmShr(op); + + long roundConst = 1L << (shift - 1); + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> op.Size : 1; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size, signed); + + if (op.Size <= 2) + { + if (round) + { + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + } + + context.EmitLdc_I4(shift); + + context.Emit(signed ? OpCodes.Shr : OpCodes.Shr_Un); + } + else /* if (op.Size == 3) */ + { + EmitShrImm64(context, signed, round ? roundConst : 0L, shift); + } + + if (accumulate) + { + EmitVectorExtract(context, op.Rd, index, op.Size, signed); + + context.Emit(OpCodes.Add); + } + + EmitVectorInsertTmp(context, index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitVectorShrImmNarrowOpZx(ILEmitterCtx context, bool round) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + int shift = GetImmShr(op); + + long roundConst = 1L << (shift - 1); + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractZx(context, op.Rn, index, op.Size + 1); + + if (round) + { + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + } + + context.EmitLdc_I4(shift); + + context.Emit(OpCodes.Shr_Un); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + [Flags] + private enum ShrImmSaturatingNarrowFlags + { + Scalar = 1 << 0, + SignedSrc = 1 << 1, + SignedDst = 1 << 2, + + Round = 1 << 3, + + ScalarSxSx = Scalar | SignedSrc | SignedDst, + ScalarSxZx = Scalar | SignedSrc, + ScalarZxZx = Scalar, + + VectorSxSx = SignedSrc | SignedDst, + VectorSxZx = SignedSrc, + VectorZxZx = 0 + } + + private static void EmitRoundShrImmSaturatingNarrowOp(ILEmitterCtx context, ShrImmSaturatingNarrowFlags flags) + { + EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.Round | flags); + } + + private static void EmitShrImmSaturatingNarrowOp(ILEmitterCtx context, ShrImmSaturatingNarrowFlags flags) + { + OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp; + + bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0; + bool signedSrc = (flags & ShrImmSaturatingNarrowFlags.SignedSrc) != 0; + bool signedDst = (flags & ShrImmSaturatingNarrowFlags.SignedDst) != 0; + bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0; + + int shift = GetImmShr(op); + + long roundConst = 1L << (shift - 1); + + int elems = !scalar ? 8 >> op.Size : 1; + + int part = !scalar && (op.RegisterSize == RegisterSize.Simd128) ? elems : 0; + + if (scalar) + { + EmitVectorZeroLowerTmp(context); + } + + if (part != 0) + { + context.EmitLdvec(op.Rd); + context.EmitStvectmp(); + } + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, index, op.Size + 1, signedSrc); + + if (op.Size <= 1 || !round) + { + if (round) + { + context.EmitLdc_I8(roundConst); + + context.Emit(OpCodes.Add); + } + + context.EmitLdc_I4(shift); + + context.Emit(signedSrc ? OpCodes.Shr : OpCodes.Shr_Un); + } + else /* if (op.Size == 2 && round) */ + { + EmitShrImm64(context, signedSrc, roundConst, shift); // shift <= 32 + } + + EmitSatQ(context, op.Size, signedSrc, signedDst); + + EmitVectorInsertTmp(context, part + index, op.Size); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + + if (part == 0) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + // dst64 = (Int(src64, signed) + roundConst) >> shift; + private static void EmitShrImm64( + ILEmitterCtx context, + bool signed, + long roundConst, + int shift) + { + context.EmitLdc_I8(roundConst); + context.EmitLdc_I4(shift); + + SoftFallback.EmitCall(context, signed + ? nameof(SoftFallback.SignedShrImm64) + : nameof(SoftFallback.UnsignedShrImm64)); + } + + private static void EmitVectorShImmWidenBinarySx(ILEmitterCtx context, Action emit, int imm) + { + EmitVectorShImmWidenBinaryOp(context, emit, imm, true); + } + + private static void EmitVectorShImmWidenBinaryZx(ILEmitterCtx context, Action emit, int imm) + { + EmitVectorShImmWidenBinaryOp(context, emit, imm, false); + } + + private static void EmitVectorShImmWidenBinaryOp(ILEmitterCtx context, Action emit, int imm, bool signed) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int elems = 8 >> op.Size; + + int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtract(context, op.Rn, part + index, op.Size, signed); + + context.EmitLdc_I4(imm); + + emit(); + + EmitVectorInsertTmp(context, index, op.Size + 1); + } + + context.EmitLdvectmp(); + context.EmitStvec(op.Rd); + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitSystem.cs b/ChocolArm64/Instructions/InstEmitSystem.cs new file mode 100644 index 0000000000..0e61d5bded --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitSystem.cs @@ -0,0 +1,138 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Instructions +{ + static partial class InstEmit + { + public static void Hint(ILEmitterCtx context) + { + //Execute as no-op. + } + + public static void Isb(ILEmitterCtx context) + { + //Execute as no-op. + } + + public static void Mrs(ILEmitterCtx context) + { + OpCodeSystem64 op = (OpCodeSystem64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.StateArgIdx); + + string propName; + + switch (GetPackedId(op)) + { + case 0b11_011_0000_0000_001: propName = nameof(CpuThreadState.CtrEl0); break; + case 0b11_011_0000_0000_111: propName = nameof(CpuThreadState.DczidEl0); break; + case 0b11_011_0100_0100_000: propName = nameof(CpuThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: propName = nameof(CpuThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: propName = nameof(CpuThreadState.TpidrEl0); break; + case 0b11_011_1101_0000_011: propName = nameof(CpuThreadState.Tpidr); break; + case 0b11_011_1110_0000_000: propName = nameof(CpuThreadState.CntfrqEl0); break; + case 0b11_011_1110_0000_001: propName = nameof(CpuThreadState.CntpctEl0); break; + + default: throw new NotImplementedException($"Unknown MRS at {op.Position:x16}"); + } + + context.EmitCallPropGet(typeof(CpuThreadState), propName); + + PropertyInfo propInfo = typeof(CpuThreadState).GetProperty(propName); + + if (propInfo.PropertyType != typeof(long) && + propInfo.PropertyType != typeof(ulong)) + { + context.Emit(OpCodes.Conv_U8); + } + + context.EmitStintzr(op.Rt); + } + + public static void Msr(ILEmitterCtx context) + { + OpCodeSystem64 op = (OpCodeSystem64)context.CurrOp; + + context.EmitLdarg(TranslatedSub.StateArgIdx); + context.EmitLdintzr(op.Rt); + + string propName; + + switch (GetPackedId(op)) + { + case 0b11_011_0100_0100_000: propName = nameof(CpuThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: propName = nameof(CpuThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: propName = nameof(CpuThreadState.TpidrEl0); break; + + default: throw new NotImplementedException($"Unknown MSR at {op.Position:x16}"); + } + + PropertyInfo propInfo = typeof(CpuThreadState).GetProperty(propName); + + if (propInfo.PropertyType != typeof(long) && + propInfo.PropertyType != typeof(ulong)) + { + context.Emit(OpCodes.Conv_U4); + } + + context.EmitCallPropSet(typeof(CpuThreadState), propName); + } + + public static void Nop(ILEmitterCtx context) + { + //Do nothing. + } + + public static void Sys(ILEmitterCtx context) + { + //This instruction is used to do some operations on the CPU like cache invalidation, + //address translation and the like. + //We treat it as no-op here since we don't have any cache being emulated anyway. + OpCodeSystem64 op = (OpCodeSystem64)context.CurrOp; + + switch (GetPackedId(op)) + { + case 0b11_011_0111_0100_001: + { + //DC ZVA + for (int offs = 0; offs < (4 << CpuThreadState.DczSizeLog2); offs += 8) + { + context.EmitLdarg(TranslatedSub.MemoryArgIdx); + context.EmitLdintzr(op.Rt); + context.EmitLdc_I(offs); + + context.Emit(OpCodes.Add); + + context.EmitLdc_I8(0); + + InstEmitMemoryHelper.EmitWriteCall(context, 3); + } + + break; + } + + //No-op + case 0b11_011_0111_1110_001: //DC CIVAC + break; + } + } + + private static int GetPackedId(OpCodeSystem64 op) + { + int id; + + id = op.Op2 << 0; + id |= op.CRm << 3; + id |= op.CRn << 7; + id |= op.Op1 << 11; + id |= op.Op0 << 14; + + return id; + } + } +} diff --git a/ChocolArm64/Instructions/InstEmitter.cs b/ChocolArm64/Instructions/InstEmitter.cs new file mode 100644 index 0000000000..db6e8604f8 --- /dev/null +++ b/ChocolArm64/Instructions/InstEmitter.cs @@ -0,0 +1,6 @@ +using ChocolArm64.Translation; + +namespace ChocolArm64.Instructions +{ + delegate void InstEmitter(ILEmitterCtx context); +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstInterpreter.cs b/ChocolArm64/Instructions/InstInterpreter.cs new file mode 100644 index 0000000000..e6354fd5a7 --- /dev/null +++ b/ChocolArm64/Instructions/InstInterpreter.cs @@ -0,0 +1,8 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Memory; +using ChocolArm64.State; + +namespace ChocolArm64.Instructions +{ + delegate void InstInterpreter(CpuThreadState state, MemoryManager memory, OpCode64 opCode); +} \ No newline at end of file diff --git a/ChocolArm64/Instructions/SoftFallback.cs b/ChocolArm64/Instructions/SoftFallback.cs new file mode 100644 index 0000000000..8315395a71 --- /dev/null +++ b/ChocolArm64/Instructions/SoftFallback.cs @@ -0,0 +1,922 @@ +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace ChocolArm64.Instructions +{ + using static VectorHelper; + + static class SoftFallback + { + public static void EmitCall(ILEmitterCtx context, string mthdName) + { + context.EmitCall(typeof(SoftFallback), mthdName); + } + +#region "ShrImm64" + public static long SignedShrImm64(long value, long roundConst, int shift) + { + if (roundConst == 0L) + { + if (shift <= 63) + { + return value >> shift; + } + else /* if (shift == 64) */ + { + if (value < 0L) + { + return -1L; + } + else + { + return 0L; + } + } + } + else /* if (roundConst == 1L << (shift - 1)) */ + { + if (shift <= 63) + { + long add = value + roundConst; + + if ((~value & (value ^ add)) < 0L) + { + return (long)((ulong)add >> shift); + } + else + { + return add >> shift; + } + } + else /* if (shift == 64) */ + { + return 0L; + } + } + } + + public static ulong UnsignedShrImm64(ulong value, long roundConst, int shift) + { + if (roundConst == 0L) + { + if (shift <= 63) + { + return value >> shift; + } + else /* if (shift == 64) */ + { + return 0UL; + } + } + else /* if (roundConst == 1L << (shift - 1)) */ + { + ulong add = value + (ulong)roundConst; + + if ((add < value) && (add < (ulong)roundConst)) + { + if (shift <= 63) + { + return (add >> shift) | (0x8000000000000000UL >> (shift - 1)); + } + else /* if (shift == 64) */ + { + return 1UL; + } + } + else + { + if (shift <= 63) + { + return add >> shift; + } + else /* if (shift == 64) */ + { + return 0UL; + } + } + } + } +#endregion + +#region "Saturating" + public static long SignedSrcSignedDstSatQ(long op, int size, CpuThreadState state) + { + int eSize = 8 << size; + + long tMaxValue = (1L << (eSize - 1)) - 1L; + long tMinValue = -(1L << (eSize - 1)); + + if (op > tMaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMaxValue; + } + else if (op < tMinValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMinValue; + } + else + { + return op; + } + } + + public static ulong SignedSrcUnsignedDstSatQ(long op, int size, CpuThreadState state) + { + int eSize = 8 << size; + + ulong tMaxValue = (1UL << eSize) - 1UL; + ulong tMinValue = 0UL; + + if (op > (long)tMaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMaxValue; + } + else if (op < (long)tMinValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMinValue; + } + else + { + return (ulong)op; + } + } + + public static long UnsignedSrcSignedDstSatQ(ulong op, int size, CpuThreadState state) + { + int eSize = 8 << size; + + long tMaxValue = (1L << (eSize - 1)) - 1L; + + if (op > (ulong)tMaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMaxValue; + } + else + { + return (long)op; + } + } + + public static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int size, CpuThreadState state) + { + int eSize = 8 << size; + + ulong tMaxValue = (1UL << eSize) - 1UL; + + if (op > tMaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return tMaxValue; + } + else + { + return op; + } + } + + public static long UnarySignedSatQAbsOrNeg(long op, CpuThreadState state) + { + if (op == long.MinValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return long.MaxValue; + } + else + { + return op; + } + } + + public static long BinarySignedSatQAdd(long op1, long op2, CpuThreadState state) + { + long add = op1 + op2; + + if ((~(op1 ^ op2) & (op1 ^ add)) < 0L) + { + state.SetFpsrFlag(Fpsr.Qc); + + if (op1 < 0L) + { + return long.MinValue; + } + else + { + return long.MaxValue; + } + } + else + { + return add; + } + } + + public static ulong BinaryUnsignedSatQAdd(ulong op1, ulong op2, CpuThreadState state) + { + ulong add = op1 + op2; + + if ((add < op1) && (add < op2)) + { + state.SetFpsrFlag(Fpsr.Qc); + + return ulong.MaxValue; + } + else + { + return add; + } + } + + public static long BinarySignedSatQSub(long op1, long op2, CpuThreadState state) + { + long sub = op1 - op2; + + if (((op1 ^ op2) & (op1 ^ sub)) < 0L) + { + state.SetFpsrFlag(Fpsr.Qc); + + if (op1 < 0L) + { + return long.MinValue; + } + else + { + return long.MaxValue; + } + } + else + { + return sub; + } + } + + public static ulong BinaryUnsignedSatQSub(ulong op1, ulong op2, CpuThreadState state) + { + ulong sub = op1 - op2; + + if (op1 < op2) + { + state.SetFpsrFlag(Fpsr.Qc); + + return ulong.MinValue; + } + else + { + return sub; + } + } + + public static long BinarySignedSatQAcc(ulong op1, long op2, CpuThreadState state) + { + if (op1 <= (ulong)long.MaxValue) + { + // op1 from ulong.MinValue to (ulong)long.MaxValue + // op2 from long.MinValue to long.MaxValue + + long add = (long)op1 + op2; + + if ((~op2 & add) < 0L) + { + state.SetFpsrFlag(Fpsr.Qc); + + return long.MaxValue; + } + else + { + return add; + } + } + else if (op2 >= 0L) + { + // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + // op2 from (long)ulong.MinValue to long.MaxValue + + state.SetFpsrFlag(Fpsr.Qc); + + return long.MaxValue; + } + else + { + // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + // op2 from long.MinValue to (long)ulong.MinValue - 1L + + ulong add = op1 + (ulong)op2; + + if (add > (ulong)long.MaxValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return long.MaxValue; + } + else + { + return (long)add; + } + } + } + + public static ulong BinaryUnsignedSatQAcc(long op1, ulong op2, CpuThreadState state) + { + if (op1 >= 0L) + { + // op1 from (long)ulong.MinValue to long.MaxValue + // op2 from ulong.MinValue to ulong.MaxValue + + ulong add = (ulong)op1 + op2; + + if ((add < (ulong)op1) && (add < op2)) + { + state.SetFpsrFlag(Fpsr.Qc); + + return ulong.MaxValue; + } + else + { + return add; + } + } + else if (op2 > (ulong)long.MaxValue) + { + // op1 from long.MinValue to (long)ulong.MinValue - 1L + // op2 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + + return (ulong)op1 + op2; + } + else + { + // op1 from long.MinValue to (long)ulong.MinValue - 1L + // op2 from ulong.MinValue to (ulong)long.MaxValue + + long add = op1 + (long)op2; + + if (add < (long)ulong.MinValue) + { + state.SetFpsrFlag(Fpsr.Qc); + + return ulong.MinValue; + } + else + { + return (ulong)add; + } + } + } +#endregion + +#region "Count" + public static ulong CountLeadingSigns(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). + { + value ^= value >> 1; + + int highBit = size - 2; + + for (int bit = highBit; bit >= 0; bit--) + { + if (((value >> bit) & 0b1) != 0) + { + return (ulong)(highBit - bit); + } + } + + return (ulong)(size - 1); + } + + private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; + + public static ulong CountLeadingZeros(ulong value, int size) // size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). + { + if (value == 0ul) + { + return (ulong)size; + } + + int nibbleIdx = size; + int preCount, count = 0; + + do + { + nibbleIdx -= 4; + preCount = ClzNibbleTbl[(value >> nibbleIdx) & 0b1111]; + count += preCount; + } + while (preCount == 4); + + return (ulong)count; + } + + public static ulong CountSetBits8(ulong value) // "size" is 8 (SIMD&FP Inst.). + { + if (value == 0xfful) + { + return 8ul; + } + + value = ((value >> 1) & 0x55ul) + (value & 0x55ul); + value = ((value >> 2) & 0x33ul) + (value & 0x33ul); + + return (value >> 4) + (value & 0x0ful); + } +#endregion + +#region "Crc32" + private const uint Crc32RevPoly = 0xedb88320; + private const uint Crc32CRevPoly = 0x82f63b78; + + public static uint Crc32B(uint crc, byte val) => Crc32 (crc, Crc32RevPoly, val); + public static uint Crc32H(uint crc, ushort val) => Crc32H(crc, Crc32RevPoly, val); + public static uint Crc32W(uint crc, uint val) => Crc32W(crc, Crc32RevPoly, val); + public static uint Crc32X(uint crc, ulong val) => Crc32X(crc, Crc32RevPoly, val); + + public static uint Crc32Cb(uint crc, byte val) => Crc32 (crc, Crc32CRevPoly, val); + public static uint Crc32Ch(uint crc, ushort val) => Crc32H(crc, Crc32CRevPoly, val); + public static uint Crc32Cw(uint crc, uint val) => Crc32W(crc, Crc32CRevPoly, val); + public static uint Crc32Cx(uint crc, ulong val) => Crc32X(crc, Crc32CRevPoly, val); + + private static uint Crc32H(uint crc, uint poly, ushort val) + { + crc = Crc32(crc, poly, (byte)(val >> 0)); + crc = Crc32(crc, poly, (byte)(val >> 8)); + + return crc; + } + + private static uint Crc32W(uint crc, uint poly, uint val) + { + crc = Crc32(crc, poly, (byte)(val >> 0 )); + crc = Crc32(crc, poly, (byte)(val >> 8 )); + crc = Crc32(crc, poly, (byte)(val >> 16)); + crc = Crc32(crc, poly, (byte)(val >> 24)); + + return crc; + } + + private static uint Crc32X(uint crc, uint poly, ulong val) + { + crc = Crc32(crc, poly, (byte)(val >> 0 )); + crc = Crc32(crc, poly, (byte)(val >> 8 )); + crc = Crc32(crc, poly, (byte)(val >> 16)); + crc = Crc32(crc, poly, (byte)(val >> 24)); + crc = Crc32(crc, poly, (byte)(val >> 32)); + crc = Crc32(crc, poly, (byte)(val >> 40)); + crc = Crc32(crc, poly, (byte)(val >> 48)); + crc = Crc32(crc, poly, (byte)(val >> 56)); + + return crc; + } + + private static uint Crc32(uint crc, uint poly, byte val) + { + crc ^= val; + + for (int bit = 7; bit >= 0; bit--) + { + uint mask = (uint)(-(int)(crc & 1)); + + crc = (crc >> 1) ^ (poly & mask); + } + + return crc; + } +#endregion + +#region "Aes" + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Decrypt(Vector128 value, Vector128 roundKey) + { + if (!Sse.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + return CryptoHelper.AesInvSubBytes(CryptoHelper.AesInvShiftRows(Sse.Xor(value, roundKey))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Encrypt(Vector128 value, Vector128 roundKey) + { + if (!Sse.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + return CryptoHelper.AesSubBytes(CryptoHelper.AesShiftRows(Sse.Xor(value, roundKey))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 InverseMixColumns(Vector128 value) + { + return CryptoHelper.AesInvMixColumns(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 MixColumns(Vector128 value) + { + return CryptoHelper.AesMixColumns(value); + } +#endregion + +#region "Sha1" + public static Vector128 HashChoose(Vector128 hash_abcd, uint hash_e, Vector128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaChoose((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), + (uint)VectorExtractIntZx(hash_abcd, (byte)2, 2), + (uint)VectorExtractIntZx(hash_abcd, (byte)3, 2)); + + hash_e += Rol((uint)VectorExtractIntZx(hash_abcd, (byte)0, 2), 5) + t; + hash_e += (uint)VectorExtractIntZx(wk, (byte)e, 2); + + t = Rol((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), 30); + hash_abcd = VectorInsertInt((ulong)t, hash_abcd, (byte)1, 2); + + Rol32_160(ref hash_e, ref hash_abcd); + } + + return hash_abcd; + } + + public static uint FixedRotate(uint hash_e) + { + return hash_e.Rol(30); + } + + public static Vector128 HashMajority(Vector128 hash_abcd, uint hash_e, Vector128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaMajority((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), + (uint)VectorExtractIntZx(hash_abcd, (byte)2, 2), + (uint)VectorExtractIntZx(hash_abcd, (byte)3, 2)); + + hash_e += Rol((uint)VectorExtractIntZx(hash_abcd, (byte)0, 2), 5) + t; + hash_e += (uint)VectorExtractIntZx(wk, (byte)e, 2); + + t = Rol((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), 30); + hash_abcd = VectorInsertInt((ulong)t, hash_abcd, (byte)1, 2); + + Rol32_160(ref hash_e, ref hash_abcd); + } + + return hash_abcd; + } + + public static Vector128 HashParity(Vector128 hash_abcd, uint hash_e, Vector128 wk) + { + for (int e = 0; e <= 3; e++) + { + uint t = ShaParity((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), + (uint)VectorExtractIntZx(hash_abcd, (byte)2, 2), + (uint)VectorExtractIntZx(hash_abcd, (byte)3, 2)); + + hash_e += Rol((uint)VectorExtractIntZx(hash_abcd, (byte)0, 2), 5) + t; + hash_e += (uint)VectorExtractIntZx(wk, (byte)e, 2); + + t = Rol((uint)VectorExtractIntZx(hash_abcd, (byte)1, 2), 30); + hash_abcd = VectorInsertInt((ulong)t, hash_abcd, (byte)1, 2); + + Rol32_160(ref hash_e, ref hash_abcd); + } + + return hash_abcd; + } + + public static Vector128 Sha1SchedulePart1(Vector128 w0_3, Vector128 w4_7, Vector128 w8_11) + { + if (!Sse.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + Vector128 result = new Vector128(); + + ulong t2 = VectorExtractIntZx(w4_7, (byte)0, 3); + ulong t1 = VectorExtractIntZx(w0_3, (byte)1, 3); + + result = VectorInsertInt((ulong)t1, result, (byte)0, 3); + result = VectorInsertInt((ulong)t2, result, (byte)1, 3); + + return Sse.Xor(result, Sse.Xor(w0_3, w8_11)); + } + + public static Vector128 Sha1SchedulePart2(Vector128 tw0_3, Vector128 w12_15) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + Vector128 result = new Vector128(); + + Vector128 t = Sse.Xor(tw0_3, Sse.StaticCast( + Sse2.ShiftRightLogical128BitLane(Sse.StaticCast(w12_15), (byte)4))); + + uint tE0 = (uint)VectorExtractIntZx(t, (byte)0, 2); + uint tE1 = (uint)VectorExtractIntZx(t, (byte)1, 2); + uint tE2 = (uint)VectorExtractIntZx(t, (byte)2, 2); + uint tE3 = (uint)VectorExtractIntZx(t, (byte)3, 2); + + result = VectorInsertInt((ulong)tE0.Rol(1), result, (byte)0, 2); + result = VectorInsertInt((ulong)tE1.Rol(1), result, (byte)1, 2); + result = VectorInsertInt((ulong)tE2.Rol(1), result, (byte)2, 2); + + return VectorInsertInt((ulong)(tE3.Rol(1) ^ tE0.Rol(2)), result, (byte)3, 2); + } + + private static void Rol32_160(ref uint y, ref Vector128 x) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + uint xE3 = (uint)VectorExtractIntZx(x, (byte)3, 2); + + x = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(x), (byte)4)); + x = VectorInsertInt((ulong)y, x, (byte)0, 2); + + y = xE3; + } + + private static uint ShaChoose(uint x, uint y, uint z) + { + return ((y ^ z) & x) ^ z; + } + + private static uint ShaMajority(uint x, uint y, uint z) + { + return (x & y) | ((x | y) & z); + } + + private static uint ShaParity(uint x, uint y, uint z) + { + return x ^ y ^ z; + } + + private static uint Rol(this uint value, int count) + { + return (value << count) | (value >> (32 - count)); + } +#endregion + +#region "Sha256" + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 HashLower(Vector128 hash_abcd, Vector128 hash_efgh, Vector128 wk) + { + return Sha256Hash(hash_abcd, hash_efgh, wk, true); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 HashUpper(Vector128 hash_efgh, Vector128 hash_abcd, Vector128 wk) + { + return Sha256Hash(hash_abcd, hash_efgh, wk, false); + } + + public static Vector128 Sha256SchedulePart1(Vector128 w0_3, Vector128 w4_7) + { + Vector128 result = new Vector128(); + + for (int e = 0; e <= 3; e++) + { + uint elt = (uint)VectorExtractIntZx(e <= 2 ? w0_3 : w4_7, (byte)(e <= 2 ? e + 1 : 0), 2); + + elt = elt.Ror(7) ^ elt.Ror(18) ^ elt.Lsr(3); + + elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); + + result = VectorInsertInt((ulong)elt, result, (byte)e, 2); + } + + return result; + } + + public static Vector128 Sha256SchedulePart2(Vector128 w0_3, Vector128 w8_11, Vector128 w12_15) + { + Vector128 result = new Vector128(); + + ulong t1 = VectorExtractIntZx(w12_15, (byte)1, 3); + + for (int e = 0; e <= 1; e++) + { + uint elt = t1.ULongPart(e); + + elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); + + elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); + elt += (uint)VectorExtractIntZx(w8_11, (byte)(e + 1), 2); + + result = VectorInsertInt((ulong)elt, result, (byte)e, 2); + } + + t1 = VectorExtractIntZx(result, (byte)0, 3); + + for (int e = 2; e <= 3; e++) + { + uint elt = t1.ULongPart(e - 2); + + elt = elt.Ror(17) ^ elt.Ror(19) ^ elt.Lsr(10); + + elt += (uint)VectorExtractIntZx(w0_3, (byte)e, 2); + elt += (uint)VectorExtractIntZx(e == 2 ? w8_11 : w12_15, (byte)(e == 2 ? 3 : 0), 2); + + result = VectorInsertInt((ulong)elt, result, (byte)e, 2); + } + + return result; + } + + private static Vector128 Sha256Hash(Vector128 x, Vector128 y, Vector128 w, bool part1) + { + for (int e = 0; e <= 3; e++) + { + uint chs = ShaChoose((uint)VectorExtractIntZx(y, (byte)0, 2), + (uint)VectorExtractIntZx(y, (byte)1, 2), + (uint)VectorExtractIntZx(y, (byte)2, 2)); + + uint maj = ShaMajority((uint)VectorExtractIntZx(x, (byte)0, 2), + (uint)VectorExtractIntZx(x, (byte)1, 2), + (uint)VectorExtractIntZx(x, (byte)2, 2)); + + uint t1 = (uint)VectorExtractIntZx(y, (byte)3, 2); + t1 += ShaHashSigma1((uint)VectorExtractIntZx(y, (byte)0, 2)) + chs; + t1 += (uint)VectorExtractIntZx(w, (byte)e, 2); + + uint t2 = t1 + (uint)VectorExtractIntZx(x, (byte)3, 2); + x = VectorInsertInt((ulong)t2, x, (byte)3, 2); + t2 = t1 + ShaHashSigma0((uint)VectorExtractIntZx(x, (byte)0, 2)) + maj; + y = VectorInsertInt((ulong)t2, y, (byte)3, 2); + + Rol32_256(ref y, ref x); + } + + return part1 ? x : y; + } + + private static void Rol32_256(ref Vector128 y, ref Vector128 x) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + uint yE3 = (uint)VectorExtractIntZx(y, (byte)3, 2); + uint xE3 = (uint)VectorExtractIntZx(x, (byte)3, 2); + + y = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(y), (byte)4)); + x = Sse.StaticCast(Sse2.ShiftLeftLogical128BitLane(Sse.StaticCast(x), (byte)4)); + + y = VectorInsertInt((ulong)xE3, y, (byte)0, 2); + x = VectorInsertInt((ulong)yE3, x, (byte)0, 2); + } + + private static uint ShaHashSigma0(uint x) + { + return x.Ror(2) ^ x.Ror(13) ^ x.Ror(22); + } + + private static uint ShaHashSigma1(uint x) + { + return x.Ror(6) ^ x.Ror(11) ^ x.Ror(25); + } + + private static uint Ror(this uint value, int count) + { + return (value >> count) | (value << (32 - count)); + } + + private static uint Lsr(this uint value, int count) + { + return value >> count; + } + + private static uint ULongPart(this ulong value, int part) + { + return part == 0 + ? (uint)(value & 0xFFFFFFFFUL) + : (uint)(value >> 32); + } +#endregion + +#region "Reverse" + public static uint ReverseBits8(uint value) + { + value = ((value & 0xaa) >> 1) | ((value & 0x55) << 1); + value = ((value & 0xcc) >> 2) | ((value & 0x33) << 2); + + return (value >> 4) | ((value & 0x0f) << 4); + } + + public static uint ReverseBits32(uint value) + { + value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1); + value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2); + value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4); + value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8); + + return (value >> 16) | (value << 16); + } + + public static ulong ReverseBits64(ulong value) + { + value = ((value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((value & 0x5555555555555555) << 1 ); + value = ((value & 0xcccccccccccccccc) >> 2 ) | ((value & 0x3333333333333333) << 2 ); + value = ((value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((value & 0x0f0f0f0f0f0f0f0f) << 4 ); + value = ((value & 0xff00ff00ff00ff00) >> 8 ) | ((value & 0x00ff00ff00ff00ff) << 8 ); + value = ((value & 0xffff0000ffff0000) >> 16) | ((value & 0x0000ffff0000ffff) << 16); + + return (value >> 32) | (value << 32); + } + + public static uint ReverseBytes16_32(uint value) => (uint)ReverseBytes16_64(value); + public static uint ReverseBytes32_32(uint value) => (uint)ReverseBytes32_64(value); + + public static ulong ReverseBytes16_64(ulong value) => ReverseBytes(value, RevSize.Rev16); + public static ulong ReverseBytes32_64(ulong value) => ReverseBytes(value, RevSize.Rev32); + public static ulong ReverseBytes64(ulong value) => ReverseBytes(value, RevSize.Rev64); + + private enum RevSize + { + Rev16, + Rev32, + Rev64 + } + + private static ulong ReverseBytes(ulong value, RevSize size) + { + value = ((value & 0xff00ff00ff00ff00) >> 8) | ((value & 0x00ff00ff00ff00ff) << 8); + + if (size == RevSize.Rev16) + { + return value; + } + + value = ((value & 0xffff0000ffff0000) >> 16) | ((value & 0x0000ffff0000ffff) << 16); + + if (size == RevSize.Rev32) + { + return value; + } + + value = ((value & 0xffffffff00000000) >> 32) | ((value & 0x00000000ffffffff) << 32); + + if (size == RevSize.Rev64) + { + return value; + } + + throw new ArgumentException(nameof(size)); + } +#endregion + +#region "MultiplyHigh" + public static long SMulHi128(long left, long right) + { + long result = (long)UMulHi128((ulong)left, (ulong)right); + + if (left < 0) + { + result -= right; + } + + if (right < 0) + { + result -= left; + } + + return result; + } + + public static ulong UMulHi128(ulong left, ulong right) + { + ulong lHigh = left >> 32; + ulong lLow = left & 0xFFFFFFFF; + ulong rHigh = right >> 32; + ulong rLow = right & 0xFFFFFFFF; + + ulong z2 = lLow * rLow; + ulong t = lHigh * rLow + (z2 >> 32); + ulong z1 = t & 0xFFFFFFFF; + ulong z0 = t >> 32; + + z1 += lLow * rHigh; + + return lHigh * rHigh + z0 + (z1 >> 32); + } +#endregion + } +} diff --git a/ChocolArm64/Instructions/SoftFloat.cs b/ChocolArm64/Instructions/SoftFloat.cs new file mode 100644 index 0000000000..2af8afbd03 --- /dev/null +++ b/ChocolArm64/Instructions/SoftFloat.cs @@ -0,0 +1,2405 @@ +using ChocolArm64.State; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace ChocolArm64.Instructions +{ + static class SoftFloat + { + static SoftFloat() + { + RecipEstimateTable = BuildRecipEstimateTable(); + InvSqrtEstimateTable = BuildInvSqrtEstimateTable(); + } + + private static readonly byte[] RecipEstimateTable; + private static readonly byte[] InvSqrtEstimateTable; + + private static byte[] BuildRecipEstimateTable() + { + byte[] table = new byte[256]; + for (ulong index = 0; index < 256; index++) + { + ulong a = index | 0x100; + + a = (a << 1) + 1; + ulong b = 0x80000 / a; + b = (b + 1) >> 1; + + table[index] = (byte)(b & 0xFF); + } + return table; + } + + private static byte[] BuildInvSqrtEstimateTable() + { + byte[] table = new byte[512]; + for (ulong index = 128; index < 512; index++) + { + ulong a = index; + if (a < 256) + { + a = (a << 1) + 1; + } + else + { + a = (a | 1) << 1; + } + + ulong b = 256; + while (a * (b + 1) * (b + 1) < (1ul << 28)) + { + b++; + } + b = (b + 1) >> 1; + + table[index] = (byte)(b & 0xFF); + } + return table; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float RecipEstimate(float x) + { + return (float)RecipEstimate((double)x); + } + + public static double RecipEstimate(double x) + { + ulong xBits = (ulong)BitConverter.DoubleToInt64Bits(x); + ulong xSign = xBits & 0x8000000000000000; + ulong xExp = (xBits >> 52) & 0x7FF; + ulong scaled = xBits & ((1ul << 52) - 1); + + if (xExp >= 2045) + { + if (xExp == 0x7ff && scaled != 0) + { + // NaN + return BitConverter.Int64BitsToDouble((long)(xBits | 0x0008000000000000)); + } + + // Infinity, or Out of range -> Zero + return BitConverter.Int64BitsToDouble((long)xSign); + } + + if (xExp == 0) + { + if (scaled == 0) + { + // Zero -> Infinity + return BitConverter.Int64BitsToDouble((long)(xSign | 0x7FF0000000000000)); + } + + // Denormal + if ((scaled & (1ul << 51)) == 0) + { + xExp = ~0ul; + scaled <<= 2; + } + else + { + scaled <<= 1; + } + } + + scaled >>= 44; + scaled &= 0xFF; + + ulong resultExp = (2045 - xExp) & 0x7FF; + ulong estimate = (ulong)RecipEstimateTable[scaled]; + ulong fraction = estimate << 44; + + if (resultExp == 0) + { + fraction >>= 1; + fraction |= 1ul << 51; + } + else if (resultExp == 0x7FF) + { + resultExp = 0; + fraction >>= 2; + fraction |= 1ul << 50; + } + + ulong result = xSign | (resultExp << 52) | fraction; + return BitConverter.Int64BitsToDouble((long)result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float InvSqrtEstimate(float x) + { + return (float)InvSqrtEstimate((double)x); + } + + public static double InvSqrtEstimate(double x) + { + ulong xBits = (ulong)BitConverter.DoubleToInt64Bits(x); + ulong xSign = xBits & 0x8000000000000000; + long xExp = (long)((xBits >> 52) & 0x7FF); + ulong scaled = xBits & ((1ul << 52) - 1); + + if (xExp == 0x7FF && scaled != 0) + { + // NaN + return BitConverter.Int64BitsToDouble((long)(xBits | 0x0008000000000000)); + } + + if (xExp == 0) + { + if (scaled == 0) + { + // Zero -> Infinity + return BitConverter.Int64BitsToDouble((long)(xSign | 0x7FF0000000000000)); + } + + // Denormal + while ((scaled & (1 << 51)) == 0) + { + scaled <<= 1; + xExp--; + } + scaled <<= 1; + } + + if (xSign != 0) + { + // Negative -> NaN + return BitConverter.Int64BitsToDouble((long)0x7FF8000000000000); + } + + if (xExp == 0x7ff && scaled == 0) + { + // Infinity -> Zero + return BitConverter.Int64BitsToDouble((long)xSign); + } + + if (((ulong)xExp & 1) == 1) + { + scaled >>= 45; + scaled &= 0xFF; + scaled |= 0x80; + } + else + { + scaled >>= 44; + scaled &= 0xFF; + scaled |= 0x100; + } + + ulong resultExp = ((ulong)(3068 - xExp) / 2) & 0x7FF; + ulong estimate = (ulong)InvSqrtEstimateTable[scaled]; + ulong fraction = estimate << 44; + + ulong result = xSign | (resultExp << 52) | fraction; + return BitConverter.Int64BitsToDouble((long)result); + } + } + + static class SoftFloat16_32 + { + public static float FPConvert(ushort valueBits, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat16_32.FPConvert: state.Fpcr = 0x{state.Fpcr:X8}"); + + double real = valueBits.FPUnpackCv(out FpType type, out bool sign, state); + + float result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + if (state.GetFpcrFlag(Fpcr.Dn)) + { + result = FPDefaultNaN(); + } + else + { + result = FPConvertNaN(valueBits); + } + + if (type == FpType.SNaN) + { + FPProcessException(FpExc.InvalidOp, state); + } + } + else if (type == FpType.Infinity) + { + result = FPInfinity(sign); + } + else if (type == FpType.Zero) + { + result = FPZero(sign); + } + else + { + result = FPRoundCv(real, state); + } + + return result; + } + + private static float FPDefaultNaN() + { + return -float.NaN; + } + + private static float FPInfinity(bool sign) + { + return sign ? float.NegativeInfinity : float.PositiveInfinity; + } + + private static float FPZero(bool sign) + { + return sign ? -0f : +0f; + } + + private static float FPMaxNormal(bool sign) + { + return sign ? float.MinValue : float.MaxValue; + } + + private static double FPUnpackCv( + this ushort valueBits, + out FpType type, + out bool sign, + CpuThreadState state) + { + sign = (~(uint)valueBits & 0x8000u) == 0u; + + uint exp16 = ((uint)valueBits & 0x7C00u) >> 10; + uint frac16 = (uint)valueBits & 0x03FFu; + + double real; + + if (exp16 == 0u) + { + if (frac16 == 0u) + { + type = FpType.Zero; + real = 0d; + } + else + { + type = FpType.Nonzero; // Subnormal. + real = Math.Pow(2d, -14) * ((double)frac16 * Math.Pow(2d, -10)); + } + } + else if (exp16 == 0x1Fu && !state.GetFpcrFlag(Fpcr.Ahp)) + { + if (frac16 == 0u) + { + type = FpType.Infinity; + real = Math.Pow(2d, 1000); + } + else + { + type = (~frac16 & 0x0200u) == 0u ? FpType.QNaN : FpType.SNaN; + real = 0d; + } + } + else + { + type = FpType.Nonzero; // Normal. + real = Math.Pow(2d, (int)exp16 - 15) * (1d + (double)frac16 * Math.Pow(2d, -10)); + } + + return sign ? -real : real; + } + + private static float FPRoundCv(double real, CpuThreadState state) + { + const int minimumExp = -126; + + const int e = 8; + const int f = 23; + + bool sign; + double mantissa; + + if (real < 0d) + { + sign = true; + mantissa = -real; + } + else + { + sign = false; + mantissa = real; + } + + int exponent = 0; + + while (mantissa < 1d) + { + mantissa *= 2d; + exponent--; + } + + while (mantissa >= 2d) + { + mantissa /= 2d; + exponent++; + } + + if (state.GetFpcrFlag(Fpcr.Fz) && exponent < minimumExp) + { + state.SetFpsrFlag(Fpsr.Ufc); + + return FPZero(sign); + } + + uint biasedExp = (uint)Math.Max(exponent - minimumExp + 1, 0); + + if (biasedExp == 0u) + { + mantissa /= Math.Pow(2d, minimumExp - exponent); + } + + uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, f)); + double error = mantissa * Math.Pow(2d, f) - (double)intMant; + + if (biasedExp == 0u && (error != 0d || state.GetFpcrFlag(Fpcr.Ufe))) + { + FPProcessException(FpExc.Underflow, state); + } + + bool overflowToInf; + bool roundUp; + + switch (state.FPRoundingMode()) + { + default: + case RoundMode.ToNearest: + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + overflowToInf = true; + break; + + case RoundMode.TowardsPlusInfinity: + roundUp = (error != 0d && !sign); + overflowToInf = !sign; + break; + + case RoundMode.TowardsMinusInfinity: + roundUp = (error != 0d && sign); + overflowToInf = sign; + break; + + case RoundMode.TowardsZero: + roundUp = false; + overflowToInf = false; + break; + } + + if (roundUp) + { + intMant++; + + if (intMant == (uint)Math.Pow(2d, f)) + { + biasedExp = 1u; + } + + if (intMant == (uint)Math.Pow(2d, f + 1)) + { + biasedExp++; + intMant >>= 1; + } + } + + float result; + + if (biasedExp >= (uint)Math.Pow(2d, e) - 1u) + { + result = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); + + FPProcessException(FpExc.Overflow, state); + + error = 1d; + } + else + { + result = BitConverter.Int32BitsToSingle( + (int)((sign ? 1u : 0u) << 31 | (biasedExp & 0xFFu) << 23 | (intMant & 0x007FFFFFu))); + } + + if (error != 0d) + { + FPProcessException(FpExc.Inexact, state); + } + + return result; + } + + private static float FPConvertNaN(ushort valueBits) + { + return BitConverter.Int32BitsToSingle( + (int)(((uint)valueBits & 0x8000u) << 16 | 0x7FC00000u | ((uint)valueBits & 0x01FFu) << 13)); + } + + private static void FPProcessException(FpExc exc, CpuThreadState state) + { + int enable = (int)exc + 8; + + if ((state.Fpcr & (1 << enable)) != 0) + { + throw new NotImplementedException("Floating-point trap handling."); + } + else + { + state.Fpsr |= 1 << (int)exc; + } + } + } + + static class SoftFloat32_16 + { + public static ushort FPConvert(float value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32_16.FPConvert: state.Fpcr = 0x{state.Fpcr:X8}"); + + double real = value.FPUnpackCv(out FpType type, out bool sign, out uint valueBits, state); + + bool altHp = state.GetFpcrFlag(Fpcr.Ahp); + + ushort resultBits; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + if (altHp) + { + resultBits = FPZero(sign); + } + else if (state.GetFpcrFlag(Fpcr.Dn)) + { + resultBits = FPDefaultNaN(); + } + else + { + resultBits = FPConvertNaN(valueBits); + } + + if (type == FpType.SNaN || altHp) + { + FPProcessException(FpExc.InvalidOp, state); + } + } + else if (type == FpType.Infinity) + { + if (altHp) + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); + + FPProcessException(FpExc.InvalidOp, state); + } + else + { + resultBits = FPInfinity(sign); + } + } + else if (type == FpType.Zero) + { + resultBits = FPZero(sign); + } + else + { + resultBits = FPRoundCv(real, state); + } + + return resultBits; + } + + private static ushort FPDefaultNaN() + { + return (ushort)0x7E00u; + } + + private static ushort FPInfinity(bool sign) + { + return sign ? (ushort)0xFC00u : (ushort)0x7C00u; + } + + private static ushort FPZero(bool sign) + { + return sign ? (ushort)0x8000u : (ushort)0x0000u; + } + + private static ushort FPMaxNormal(bool sign) + { + return sign ? (ushort)0xFBFFu : (ushort)0x7BFFu; + } + + private static double FPUnpackCv( + this float value, + out FpType type, + out bool sign, + out uint valueBits, + CpuThreadState state) + { + valueBits = (uint)BitConverter.SingleToInt32Bits(value); + + sign = (~valueBits & 0x80000000u) == 0u; + + uint exp32 = (valueBits & 0x7F800000u) >> 23; + uint frac32 = valueBits & 0x007FFFFFu; + + double real; + + if (exp32 == 0u) + { + if (frac32 == 0u || state.GetFpcrFlag(Fpcr.Fz)) + { + type = FpType.Zero; + real = 0d; + + if (frac32 != 0u) + { + FPProcessException(FpExc.InputDenorm, state); + } + } + else + { + type = FpType.Nonzero; // Subnormal. + real = Math.Pow(2d, -126) * ((double)frac32 * Math.Pow(2d, -23)); + } + } + else if (exp32 == 0xFFu) + { + if (frac32 == 0u) + { + type = FpType.Infinity; + real = Math.Pow(2d, 1000); + } + else + { + type = (~frac32 & 0x00400000u) == 0u ? FpType.QNaN : FpType.SNaN; + real = 0d; + } + } + else + { + type = FpType.Nonzero; // Normal. + real = Math.Pow(2d, (int)exp32 - 127) * (1d + (double)frac32 * Math.Pow(2d, -23)); + } + + return sign ? -real : real; + } + + private static ushort FPRoundCv(double real, CpuThreadState state) + { + const int minimumExp = -14; + + const int e = 5; + const int f = 10; + + bool sign; + double mantissa; + + if (real < 0d) + { + sign = true; + mantissa = -real; + } + else + { + sign = false; + mantissa = real; + } + + int exponent = 0; + + while (mantissa < 1d) + { + mantissa *= 2d; + exponent--; + } + + while (mantissa >= 2d) + { + mantissa /= 2d; + exponent++; + } + + uint biasedExp = (uint)Math.Max(exponent - minimumExp + 1, 0); + + if (biasedExp == 0u) + { + mantissa /= Math.Pow(2d, minimumExp - exponent); + } + + uint intMant = (uint)Math.Floor(mantissa * Math.Pow(2d, f)); + double error = mantissa * Math.Pow(2d, f) - (double)intMant; + + if (biasedExp == 0u && (error != 0d || state.GetFpcrFlag(Fpcr.Ufe))) + { + FPProcessException(FpExc.Underflow, state); + } + + bool overflowToInf; + bool roundUp; + + switch (state.FPRoundingMode()) + { + default: + case RoundMode.ToNearest: + roundUp = (error > 0.5d || (error == 0.5d && (intMant & 1u) == 1u)); + overflowToInf = true; + break; + + case RoundMode.TowardsPlusInfinity: + roundUp = (error != 0d && !sign); + overflowToInf = !sign; + break; + + case RoundMode.TowardsMinusInfinity: + roundUp = (error != 0d && sign); + overflowToInf = sign; + break; + + case RoundMode.TowardsZero: + roundUp = false; + overflowToInf = false; + break; + } + + if (roundUp) + { + intMant++; + + if (intMant == (uint)Math.Pow(2d, f)) + { + biasedExp = 1u; + } + + if (intMant == (uint)Math.Pow(2d, f + 1)) + { + biasedExp++; + intMant >>= 1; + } + } + + ushort resultBits; + + if (!state.GetFpcrFlag(Fpcr.Ahp)) + { + if (biasedExp >= (uint)Math.Pow(2d, e) - 1u) + { + resultBits = overflowToInf ? FPInfinity(sign) : FPMaxNormal(sign); + + FPProcessException(FpExc.Overflow, state); + + error = 1d; + } + else + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu)); + } + } + else + { + if (biasedExp >= (uint)Math.Pow(2d, e)) + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | 0x7FFFu); + + FPProcessException(FpExc.InvalidOp, state); + + error = 0d; + } + else + { + resultBits = (ushort)((sign ? 1u : 0u) << 15 | (biasedExp & 0x1Fu) << 10 | (intMant & 0x03FFu)); + } + } + + if (error != 0d) + { + FPProcessException(FpExc.Inexact, state); + } + + return resultBits; + } + + private static ushort FPConvertNaN(uint valueBits) + { + return (ushort)((valueBits & 0x80000000u) >> 16 | 0x7E00u | (valueBits & 0x003FE000u) >> 13); + } + + private static void FPProcessException(FpExc exc, CpuThreadState state) + { + int enable = (int)exc + 8; + + if ((state.Fpcr & (1 << enable)) != 0) + { + throw new NotImplementedException("Floating-point trap handling."); + } + else + { + state.Fpsr |= 1 << (int)exc; + } + } + } + + static class SoftFloat32 + { + public static float FPAdd(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPAdd: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if (inf1 && inf2 && sign1 == !sign2) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((inf1 && !sign1) || (inf2 && !sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 + value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + public static int FPCompare(float value1, float value2, bool signalNaNs, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPCompare: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out _, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out _, state); + + int result; + + if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN) + { + result = 0b0011; + + if (type1 == FpType.SNaN || type2 == FpType.SNaN || signalNaNs) + { + FPProcessException(FpExc.InvalidOp, state); + } + } + else + { + if (value1 == value2) + { + result = 0b0110; + } + else if (value1 < value2) + { + result = 0b1000; + } + else + { + result = 0b0010; + } + } + + return result; + } + + public static float FPDiv(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPDiv: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && inf2) || (zero1 && zero2)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if (inf1 || zero2) + { + result = FPInfinity(sign1 ^ sign2); + + if (!inf1) + { + FPProcessException(FpExc.DivideByZero, state); + } + } + else if (zero1 || inf2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 / value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + public static float FPMax(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPMax: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + if (value1 > value2) + { + if (type1 == FpType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FpType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value1; + } + } + else + { + if (type2 == FpType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FpType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + } + + return result; + } + + public static float FPMaxNum(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPMaxNum: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1.FPUnpack(out FpType type1, out _, out _, state); + value2.FPUnpack(out FpType type2, out _, out _, state); + + if (type1 == FpType.QNaN && type2 != FpType.QNaN) + { + value1 = FPInfinity(true); + } + else if (type1 != FpType.QNaN && type2 == FpType.QNaN) + { + value2 = FPInfinity(true); + } + + return FPMax(value1, value2, state); + } + + public static float FPMin(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPMin: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + if (value1 < value2) + { + if (type1 == FpType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FpType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value1; + } + } + else + { + if (type2 == FpType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FpType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + } + + return result; + } + + public static float FPMinNum(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPMinNum: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1.FPUnpack(out FpType type1, out _, out _, state); + value2.FPUnpack(out FpType type2, out _, out _, state); + + if (type1 == FpType.QNaN && type2 != FpType.QNaN) + { + value1 = FPInfinity(false); + } + else if (type1 != FpType.QNaN && type2 == FpType.QNaN) + { + value2 = FPInfinity(false); + } + + return FPMin(value1, value2, state); + } + + public static float FPMul(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPMul: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + public static float FPMulAdd( + float valueA, + float value1, + float value2, + CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPMulAdd: state.Fpcr = 0x{state.Fpcr:X8}"); + + valueA = valueA.FPUnpack(out FpType typeA, out bool signA, out uint addend, state); + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + float result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, out bool done, state); + + if (typeA == FpType.QNaN && ((inf1 && zero2) || (zero1 && inf2))) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + + if (!done) + { + bool infA = typeA == FpType.Infinity; bool zeroA = typeA == FpType.Zero; + + bool signP = sign1 ^ sign2; + bool infP = inf1 || inf2; + bool zeroP = zero1 || zero2; + + if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((infA && !signA) || (infP && !signP)) + { + result = FPInfinity(false); + } + else if ((infA && signA) || (infP && signP)) + { + result = FPInfinity(true); + } + else if (zeroA && zeroP && signA == signP) + { + result = FPZero(signA); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = valueA + (value1 * value2); + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float FPMulSub( + float valueA, + float value1, + float value2, + CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPMulSub: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + return FPMulAdd(valueA, value1, value2, state); + } + + public static float FPMulX(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPMulX: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(sign1 ^ sign2); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + public static float FPRecipStepFused(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRecipStepFused: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = 2f + (value1 * value2); + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + public static float FPRecpX(float value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRecpX: state.Fpcr = 0x{state.Fpcr:X8}"); + + value.FPUnpack(out FpType type, out bool sign, out uint op, state); + + float result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + result = FPProcessNaN(type, op, state); + } + else + { + uint notExp = (~op >> 23) & 0xFFu; + uint maxExp = 0xFEu; + + result = BitConverter.Int32BitsToSingle( + (int)((sign ? 1u : 0u) << 31 | (notExp == 0xFFu ? maxExp : notExp) << 23)); + } + + return result; + } + + public static float FPRSqrtStepFused(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPRSqrtStepFused: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPOnePointFive(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = (3f + (value1 * value2)) / 2f; + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + public static float FPSqrt(float value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPSqrt: state.Fpcr = 0x{state.Fpcr:X8}"); + + value = value.FPUnpack(out FpType type, out bool sign, out uint op, state); + + float result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + result = FPProcessNaN(type, op, state); + } + else if (type == FpType.Zero) + { + result = FPZero(sign); + } + else if (type == FpType.Infinity && !sign) + { + result = FPInfinity(sign); + } + else if (sign) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else + { + result = MathF.Sqrt(value); + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + + return result; + } + + public static float FPSub(float value1, float value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat32.FPSub: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out uint op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out uint op2, state); + + float result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if (inf1 && inf2 && sign1 == sign2) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((inf1 && !sign1) || (inf2 && sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && !sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == !sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 - value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && float.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0f); + } + } + } + + return result; + } + + private static float FPDefaultNaN() + { + return -float.NaN; + } + + private static float FPInfinity(bool sign) + { + return sign ? float.NegativeInfinity : float.PositiveInfinity; + } + + private static float FPZero(bool sign) + { + return sign ? -0f : +0f; + } + + private static float FPTwo(bool sign) + { + return sign ? -2f : +2f; + } + + private static float FPOnePointFive(bool sign) + { + return sign ? -1.5f : +1.5f; + } + + private static float FPNeg(this float value) + { + return -value; + } + + private static float FPUnpack( + this float value, + out FpType type, + out bool sign, + out uint valueBits, + CpuThreadState state) + { + valueBits = (uint)BitConverter.SingleToInt32Bits(value); + + sign = (~valueBits & 0x80000000u) == 0u; + + if ((valueBits & 0x7F800000u) == 0u) + { + if ((valueBits & 0x007FFFFFu) == 0u || state.GetFpcrFlag(Fpcr.Fz)) + { + type = FpType.Zero; + value = FPZero(sign); + + if ((valueBits & 0x007FFFFFu) != 0u) + { + FPProcessException(FpExc.InputDenorm, state); + } + } + else + { + type = FpType.Nonzero; + } + } + else if ((~valueBits & 0x7F800000u) == 0u) + { + if ((valueBits & 0x007FFFFFu) == 0u) + { + type = FpType.Infinity; + } + else + { + type = (~valueBits & 0x00400000u) == 0u ? FpType.QNaN : FpType.SNaN; + value = FPZero(sign); + } + } + else + { + type = FpType.Nonzero; + } + + return value; + } + + private static float FPProcessNaNs( + FpType type1, + FpType type2, + uint op1, + uint op2, + out bool done, + CpuThreadState state) + { + done = true; + + if (type1 == FpType.SNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.SNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type1 == FpType.QNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.QNaN) + { + return FPProcessNaN(type2, op2, state); + } + + done = false; + + return FPZero(false); + } + + private static float FPProcessNaNs3( + FpType type1, + FpType type2, + FpType type3, + uint op1, + uint op2, + uint op3, + out bool done, + CpuThreadState state) + { + done = true; + + if (type1 == FpType.SNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.SNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type3 == FpType.SNaN) + { + return FPProcessNaN(type3, op3, state); + } + else if (type1 == FpType.QNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.QNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type3 == FpType.QNaN) + { + return FPProcessNaN(type3, op3, state); + } + + done = false; + + return FPZero(false); + } + + private static float FPProcessNaN(FpType type, uint op, CpuThreadState state) + { + if (type == FpType.SNaN) + { + op |= 1u << 22; + + FPProcessException(FpExc.InvalidOp, state); + } + + if (state.GetFpcrFlag(Fpcr.Dn)) + { + return FPDefaultNaN(); + } + + return BitConverter.Int32BitsToSingle((int)op); + } + + private static void FPProcessException(FpExc exc, CpuThreadState state) + { + int enable = (int)exc + 8; + + if ((state.Fpcr & (1 << enable)) != 0) + { + throw new NotImplementedException("Floating-point trap handling."); + } + else + { + state.Fpsr |= 1 << (int)exc; + } + } + } + + static class SoftFloat64 + { + public static double FPAdd(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPAdd: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if (inf1 && inf2 && sign1 == !sign2) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((inf1 && !sign1) || (inf2 && !sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 + value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + public static int FPCompare(double value1, double value2, bool signalNaNs, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPCompare: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out _, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out _, state); + + int result; + + if (type1 == FpType.SNaN || type1 == FpType.QNaN || type2 == FpType.SNaN || type2 == FpType.QNaN) + { + result = 0b0011; + + if (type1 == FpType.SNaN || type2 == FpType.SNaN || signalNaNs) + { + FPProcessException(FpExc.InvalidOp, state); + } + } + else + { + if (value1 == value2) + { + result = 0b0110; + } + else if (value1 < value2) + { + result = 0b1000; + } + else + { + result = 0b0010; + } + } + + return result; + } + + public static double FPDiv(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPDiv: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && inf2) || (zero1 && zero2)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if (inf1 || zero2) + { + result = FPInfinity(sign1 ^ sign2); + + if (!inf1) + { + FPProcessException(FpExc.DivideByZero, state); + } + } + else if (zero1 || inf2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 / value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + public static double FPMax(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPMax: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + if (value1 > value2) + { + if (type1 == FpType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FpType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value1; + } + } + else + { + if (type2 == FpType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FpType.Zero) + { + result = FPZero(sign1 && sign2); + } + else + { + result = value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + } + + return result; + } + + public static double FPMaxNum(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPMaxNum: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1.FPUnpack(out FpType type1, out _, out _, state); + value2.FPUnpack(out FpType type2, out _, out _, state); + + if (type1 == FpType.QNaN && type2 != FpType.QNaN) + { + value1 = FPInfinity(true); + } + else if (type1 != FpType.QNaN && type2 == FpType.QNaN) + { + value2 = FPInfinity(true); + } + + return FPMax(value1, value2, state); + } + + public static double FPMin(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPMin: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + if (value1 < value2) + { + if (type1 == FpType.Infinity) + { + result = FPInfinity(sign1); + } + else if (type1 == FpType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value1; + } + } + else + { + if (type2 == FpType.Infinity) + { + result = FPInfinity(sign2); + } + else if (type2 == FpType.Zero) + { + result = FPZero(sign1 || sign2); + } + else + { + result = value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + } + + return result; + } + + public static double FPMinNum(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPMinNum: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1.FPUnpack(out FpType type1, out _, out _, state); + value2.FPUnpack(out FpType type2, out _, out _, state); + + if (type1 == FpType.QNaN && type2 != FpType.QNaN) + { + value1 = FPInfinity(false); + } + else if (type1 != FpType.QNaN && type2 == FpType.QNaN) + { + value2 = FPInfinity(false); + } + + return FPMin(value1, value2, state); + } + + public static double FPMul(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPMul: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + public static double FPMulAdd( + double valueA, + double value1, + double value2, + CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPMulAdd: state.Fpcr = 0x{state.Fpcr:X8}"); + + valueA = valueA.FPUnpack(out FpType typeA, out bool signA, out ulong addend, state); + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + double result = FPProcessNaNs3(typeA, type1, type2, addend, op1, op2, out bool done, state); + + if (typeA == FpType.QNaN && ((inf1 && zero2) || (zero1 && inf2))) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + + if (!done) + { + bool infA = typeA == FpType.Infinity; bool zeroA = typeA == FpType.Zero; + + bool signP = sign1 ^ sign2; + bool infP = inf1 || inf2; + bool zeroP = zero1 || zero2; + + if ((inf1 && zero2) || (zero1 && inf2) || (infA && infP && signA != signP)) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((infA && !signA) || (infP && !signP)) + { + result = FPInfinity(false); + } + else if ((infA && signA) || (infP && signP)) + { + result = FPInfinity(true); + } + else if (zeroA && zeroP && signA == signP) + { + result = FPZero(signA); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = valueA + (value1 * value2); + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double FPMulSub( + double valueA, + double value1, + double value2, + CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPMulSub: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + return FPMulAdd(valueA, value1, value2, state); + } + + public static double FPMulX(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPMulX: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(sign1 ^ sign2); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else if (zero1 || zero2) + { + result = FPZero(sign1 ^ sign2); + } + else + { + result = value1 * value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + public static double FPRecipStepFused(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRecipStepFused: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPTwo(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = 2d + (value1 * value2); + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + public static double FPRecpX(double value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRecpX: state.Fpcr = 0x{state.Fpcr:X8}"); + + value.FPUnpack(out FpType type, out bool sign, out ulong op, state); + + double result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + result = FPProcessNaN(type, op, state); + } + else + { + ulong notExp = (~op >> 52) & 0x7FFul; + ulong maxExp = 0x7FEul; + + result = BitConverter.Int64BitsToDouble( + (long)((sign ? 1ul : 0ul) << 63 | (notExp == 0x7FFul ? maxExp : notExp) << 52)); + } + + return result; + } + + public static double FPRSqrtStepFused(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPRSqrtStepFused: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPNeg(); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if ((inf1 && zero2) || (zero1 && inf2)) + { + result = FPOnePointFive(false); + } + else if (inf1 || inf2) + { + result = FPInfinity(sign1 ^ sign2); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + result = (3d + (value1 * value2)) / 2d; + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + public static double FPSqrt(double value, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPSqrt: state.Fpcr = 0x{state.Fpcr:X8}"); + + value = value.FPUnpack(out FpType type, out bool sign, out ulong op, state); + + double result; + + if (type == FpType.SNaN || type == FpType.QNaN) + { + result = FPProcessNaN(type, op, state); + } + else if (type == FpType.Zero) + { + result = FPZero(sign); + } + else if (type == FpType.Infinity && !sign) + { + result = FPInfinity(sign); + } + else if (sign) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else + { + result = Math.Sqrt(value); + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + + return result; + } + + public static double FPSub(double value1, double value2, CpuThreadState state) + { + Debug.WriteLineIf(state.Fpcr != 0, $"SoftFloat64.FPSub: state.Fpcr = 0x{state.Fpcr:X8}"); + + value1 = value1.FPUnpack(out FpType type1, out bool sign1, out ulong op1, state); + value2 = value2.FPUnpack(out FpType type2, out bool sign2, out ulong op2, state); + + double result = FPProcessNaNs(type1, type2, op1, op2, out bool done, state); + + if (!done) + { + bool inf1 = type1 == FpType.Infinity; bool zero1 = type1 == FpType.Zero; + bool inf2 = type2 == FpType.Infinity; bool zero2 = type2 == FpType.Zero; + + if (inf1 && inf2 && sign1 == sign2) + { + result = FPDefaultNaN(); + + FPProcessException(FpExc.InvalidOp, state); + } + else if ((inf1 && !sign1) || (inf2 && sign2)) + { + result = FPInfinity(false); + } + else if ((inf1 && sign1) || (inf2 && !sign2)) + { + result = FPInfinity(true); + } + else if (zero1 && zero2 && sign1 == !sign2) + { + result = FPZero(sign1); + } + else + { + result = value1 - value2; + + if (state.GetFpcrFlag(Fpcr.Fz) && double.IsSubnormal(result)) + { + state.SetFpsrFlag(Fpsr.Ufc); + + result = FPZero(result < 0d); + } + } + } + + return result; + } + + private static double FPDefaultNaN() + { + return -double.NaN; + } + + private static double FPInfinity(bool sign) + { + return sign ? double.NegativeInfinity : double.PositiveInfinity; + } + + private static double FPZero(bool sign) + { + return sign ? -0d : +0d; + } + + private static double FPTwo(bool sign) + { + return sign ? -2d : +2d; + } + + private static double FPOnePointFive(bool sign) + { + return sign ? -1.5d : +1.5d; + } + + private static double FPNeg(this double value) + { + return -value; + } + + private static double FPUnpack( + this double value, + out FpType type, + out bool sign, + out ulong valueBits, + CpuThreadState state) + { + valueBits = (ulong)BitConverter.DoubleToInt64Bits(value); + + sign = (~valueBits & 0x8000000000000000ul) == 0ul; + + if ((valueBits & 0x7FF0000000000000ul) == 0ul) + { + if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul || state.GetFpcrFlag(Fpcr.Fz)) + { + type = FpType.Zero; + value = FPZero(sign); + + if ((valueBits & 0x000FFFFFFFFFFFFFul) != 0ul) + { + FPProcessException(FpExc.InputDenorm, state); + } + } + else + { + type = FpType.Nonzero; + } + } + else if ((~valueBits & 0x7FF0000000000000ul) == 0ul) + { + if ((valueBits & 0x000FFFFFFFFFFFFFul) == 0ul) + { + type = FpType.Infinity; + } + else + { + type = (~valueBits & 0x0008000000000000ul) == 0ul ? FpType.QNaN : FpType.SNaN; + value = FPZero(sign); + } + } + else + { + type = FpType.Nonzero; + } + + return value; + } + + private static double FPProcessNaNs( + FpType type1, + FpType type2, + ulong op1, + ulong op2, + out bool done, + CpuThreadState state) + { + done = true; + + if (type1 == FpType.SNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.SNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type1 == FpType.QNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.QNaN) + { + return FPProcessNaN(type2, op2, state); + } + + done = false; + + return FPZero(false); + } + + private static double FPProcessNaNs3( + FpType type1, + FpType type2, + FpType type3, + ulong op1, + ulong op2, + ulong op3, + out bool done, + CpuThreadState state) + { + done = true; + + if (type1 == FpType.SNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.SNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type3 == FpType.SNaN) + { + return FPProcessNaN(type3, op3, state); + } + else if (type1 == FpType.QNaN) + { + return FPProcessNaN(type1, op1, state); + } + else if (type2 == FpType.QNaN) + { + return FPProcessNaN(type2, op2, state); + } + else if (type3 == FpType.QNaN) + { + return FPProcessNaN(type3, op3, state); + } + + done = false; + + return FPZero(false); + } + + private static double FPProcessNaN(FpType type, ulong op, CpuThreadState state) + { + if (type == FpType.SNaN) + { + op |= 1ul << 51; + + FPProcessException(FpExc.InvalidOp, state); + } + + if (state.GetFpcrFlag(Fpcr.Dn)) + { + return FPDefaultNaN(); + } + + return BitConverter.Int64BitsToDouble((long)op); + } + + private static void FPProcessException(FpExc exc, CpuThreadState state) + { + int enable = (int)exc + 8; + + if ((state.Fpcr & (1 << enable)) != 0) + { + throw new NotImplementedException("Floating-point trap handling."); + } + else + { + state.Fpsr |= 1 << (int)exc; + } + } + } +} diff --git a/ChocolArm64/Instructions/VectorHelper.cs b/ChocolArm64/Instructions/VectorHelper.cs new file mode 100644 index 0000000000..f02c131e68 --- /dev/null +++ b/ChocolArm64/Instructions/VectorHelper.cs @@ -0,0 +1,767 @@ +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace ChocolArm64.Instructions +{ + static class VectorHelper + { + public static void EmitCall(ILEmitterCtx context, string name64, string name128) + { + bool isSimd64 = context.CurrOp.RegisterSize == RegisterSize.Simd64; + + context.EmitCall(typeof(VectorHelper), isSimd64 ? name64 : name128); + } + + public static void EmitCall(ILEmitterCtx context, string mthdName) + { + context.EmitCall(typeof(VectorHelper), mthdName); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SatF32ToS32(float value) + { + if (float.IsNaN(value)) return 0; + + return value > int.MaxValue ? int.MaxValue : + value < int.MinValue ? int.MinValue : (int)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SatF32ToS64(float value) + { + if (float.IsNaN(value)) return 0; + + return value > long.MaxValue ? long.MaxValue : + value < long.MinValue ? long.MinValue : (long)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SatF32ToU32(float value) + { + if (float.IsNaN(value)) return 0; + + return value > uint.MaxValue ? uint.MaxValue : + value < uint.MinValue ? uint.MinValue : (uint)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SatF32ToU64(float value) + { + if (float.IsNaN(value)) return 0; + + return value > ulong.MaxValue ? ulong.MaxValue : + value < ulong.MinValue ? ulong.MinValue : (ulong)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SatF64ToS32(double value) + { + if (double.IsNaN(value)) return 0; + + return value > int.MaxValue ? int.MaxValue : + value < int.MinValue ? int.MinValue : (int)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SatF64ToS64(double value) + { + if (double.IsNaN(value)) return 0; + + return value > long.MaxValue ? long.MaxValue : + value < long.MinValue ? long.MinValue : (long)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SatF64ToU32(double value) + { + if (double.IsNaN(value)) return 0; + + return value > uint.MaxValue ? uint.MaxValue : + value < uint.MinValue ? uint.MinValue : (uint)value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SatF64ToU64(double value) + { + if (double.IsNaN(value)) return 0; + + return value > ulong.MaxValue ? ulong.MaxValue : + value < ulong.MinValue ? ulong.MinValue : (ulong)value; + } + + public static double Round(double value, CpuThreadState state) + { + switch (state.FPRoundingMode()) + { + case RoundMode.ToNearest: return Math.Round (value); + case RoundMode.TowardsPlusInfinity: return Math.Ceiling (value); + case RoundMode.TowardsMinusInfinity: return Math.Floor (value); + case RoundMode.TowardsZero: return Math.Truncate(value); + } + + throw new InvalidOperationException(); + } + + public static float RoundF(float value, CpuThreadState state) + { + switch (state.FPRoundingMode()) + { + case RoundMode.ToNearest: return MathF.Round (value); + case RoundMode.TowardsPlusInfinity: return MathF.Ceiling (value); + case RoundMode.TowardsMinusInfinity: return MathF.Floor (value); + case RoundMode.TowardsZero: return MathF.Truncate(value); + } + + throw new InvalidOperationException(); + } + + public static Vector128 Tbl1_V64( + Vector128 vector, + Vector128 tb0) + { + return Tbl(vector, 8, tb0); + } + + public static Vector128 Tbl1_V128( + Vector128 vector, + Vector128 tb0) + { + return Tbl(vector, 16, tb0); + } + + public static Vector128 Tbl2_V64( + Vector128 vector, + Vector128 tb0, + Vector128 tb1) + { + return Tbl(vector, 8, tb0, tb1); + } + + public static Vector128 Tbl2_V128( + Vector128 vector, + Vector128 tb0, + Vector128 tb1) + { + return Tbl(vector, 16, tb0, tb1); + } + + public static Vector128 Tbl3_V64( + Vector128 vector, + Vector128 tb0, + Vector128 tb1, + Vector128 tb2) + { + return Tbl(vector, 8, tb0, tb1, tb2); + } + + public static Vector128 Tbl3_V128( + Vector128 vector, + Vector128 tb0, + Vector128 tb1, + Vector128 tb2) + { + return Tbl(vector, 16, tb0, tb1, tb2); + } + + public static Vector128 Tbl4_V64( + Vector128 vector, + Vector128 tb0, + Vector128 tb1, + Vector128 tb2, + Vector128 tb3) + { + return Tbl(vector, 8, tb0, tb1, tb2, tb3); + } + + public static Vector128 Tbl4_V128( + Vector128 vector, + Vector128 tb0, + Vector128 tb1, + Vector128 tb2, + Vector128 tb3) + { + return Tbl(vector, 16, tb0, tb1, tb2, tb3); + } + + private static Vector128 Tbl(Vector128 vector, int bytes, params Vector128[] tb) + { + Vector128 res = new Vector128(); + + byte[] table = new byte[tb.Length * 16]; + + for (byte index = 0; index < tb.Length; index++) + for (byte index2 = 0; index2 < 16; index2++) + { + table[index * 16 + index2] = (byte)VectorExtractIntZx(tb[index], index2, 0); + } + + for (byte index = 0; index < bytes; index++) + { + byte tblIdx = (byte)VectorExtractIntZx(vector, index, 0); + + if (tblIdx < table.Length) + { + res = VectorInsertInt(table[tblIdx], res, index, 0); + } + } + + return res; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double VectorExtractDouble(Vector128 vector, byte index) + { + if (Sse41.IsSupported) + { + return BitConverter.Int64BitsToDouble(Sse41.Extract(Sse.StaticCast(vector), index)); + } + else if (Sse2.IsSupported) + { + return BitConverter.Int64BitsToDouble((long)VectorExtractIntZx(vector, index, 3)); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long VectorExtractIntSx(Vector128 vector, byte index, int size) + { + if (Sse41.IsSupported) + { + if (size == 0) + { + return (sbyte)Sse41.Extract(Sse.StaticCast(vector), index); + } + else if (size == 1) + { + return (short)Sse2.Extract(Sse.StaticCast(vector), index); + } + else if (size == 2) + { + return Sse41.Extract(Sse.StaticCast(vector), index); + } + else if (size == 3) + { + return Sse41.Extract(Sse.StaticCast(vector), index); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + else if (Sse2.IsSupported) + { + if (size == 0) + { + return (sbyte)VectorExtractIntZx(vector, index, size); + } + else if (size == 1) + { + return (short)VectorExtractIntZx(vector, index, size); + } + else if (size == 2) + { + return (int)VectorExtractIntZx(vector, index, size); + } + else if (size == 3) + { + return (long)VectorExtractIntZx(vector, index, size); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong VectorExtractIntZx(Vector128 vector, byte index, int size) + { + if (Sse41.IsSupported) + { + if (size == 0) + { + return Sse41.Extract(Sse.StaticCast(vector), index); + } + else if (size == 1) + { + return Sse2.Extract(Sse.StaticCast(vector), index); + } + else if (size == 2) + { + return Sse41.Extract(Sse.StaticCast(vector), index); + } + else if (size == 3) + { + return Sse41.Extract(Sse.StaticCast(vector), index); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + else if (Sse2.IsSupported) + { + int shortIdx = size == 0 + ? index >> 1 + : index << (size - 1); + + ushort value = Sse2.Extract(Sse.StaticCast(vector), (byte)shortIdx); + + if (size == 0) + { + return (byte)(value >> (index & 1) * 8); + } + else if (size == 1) + { + return value; + } + else if (size == 2 || size == 3) + { + ushort value1 = Sse2.Extract(Sse.StaticCast(vector), (byte)(shortIdx + 1)); + + if (size == 2) + { + return (uint)(value | (value1 << 16)); + } + + ushort value2 = Sse2.Extract(Sse.StaticCast(vector), (byte)(shortIdx + 2)); + ushort value3 = Sse2.Extract(Sse.StaticCast(vector), (byte)(shortIdx + 3)); + + return ((ulong)value << 0) | + ((ulong)value1 << 16) | + ((ulong)value2 << 32) | + ((ulong)value3 << 48); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float VectorExtractSingle(Vector128 vector, byte index) + { + if (Sse41.IsSupported) + { + return Sse41.Extract(vector, index); + } + else if (Sse2.IsSupported) + { + Vector128 shortVector = Sse.StaticCast(vector); + + int low = Sse2.Extract(shortVector, (byte)(index * 2 + 0)); + int high = Sse2.Extract(shortVector, (byte)(index * 2 + 1)); + + return BitConverter.Int32BitsToSingle(low | (high << 16)); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInsertDouble(double value, Vector128 vector, byte index) + { + return VectorInsertInt((ulong)BitConverter.DoubleToInt64Bits(value), vector, index, 3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInsertInt(ulong value, Vector128 vector, byte index, int size) + { + if (Sse41.IsSupported) + { + if (size == 0) + { + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(vector), (byte)value, index)); + } + else if (size == 1) + { + return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(vector), (ushort)value, index)); + } + else if (size == 2) + { + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(vector), (uint)value, index)); + } + else if (size == 3) + { + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(vector), value, index)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + else if (Sse2.IsSupported) + { + Vector128 shortVector = Sse.StaticCast(vector); + + int shortIdx = size == 0 + ? index >> 1 + : index << (size - 1); + + if (size == 0) + { + ushort shortVal = Sse2.Extract(Sse.StaticCast(vector), (byte)shortIdx); + + int shift = (index & 1) * 8; + + shortVal &= (ushort)(0xff00 >> shift); + + shortVal |= (ushort)((byte)value << shift); + + return Sse.StaticCast(Sse2.Insert(shortVector, shortVal, (byte)shortIdx)); + } + else if (size == 1) + { + return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(vector), (ushort)value, index)); + } + else if (size == 2 || size == 3) + { + shortVector = Sse2.Insert(shortVector, (ushort)(value >> 0), (byte)(shortIdx + 0)); + shortVector = Sse2.Insert(shortVector, (ushort)(value >> 16), (byte)(shortIdx + 1)); + + if (size == 3) + { + shortVector = Sse2.Insert(shortVector, (ushort)(value >> 32), (byte)(shortIdx + 2)); + shortVector = Sse2.Insert(shortVector, (ushort)(value >> 48), (byte)(shortIdx + 3)); + } + + return Sse.StaticCast(shortVector); + } + else + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInsertSingle(float value, Vector128 vector, byte index) + { + if (Sse41.IsSupported) + { + //Note: The if/else if is necessary to enable the JIT to + //produce a single INSERTPS instruction instead of the + //jump table fallback. + if (index == 0) + { + return Sse41.Insert(vector, value, 0x00); + } + else if (index == 1) + { + return Sse41.Insert(vector, value, 0x10); + } + else if (index == 2) + { + return Sse41.Insert(vector, value, 0x20); + } + else if (index == 3) + { + return Sse41.Insert(vector, value, 0x30); + } + else + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + } + else if (Sse2.IsSupported) + { + int intValue = BitConverter.SingleToInt32Bits(value); + + ushort low = (ushort)(intValue >> 0); + ushort high = (ushort)(intValue >> 16); + + Vector128 shortVector = Sse.StaticCast(vector); + + shortVector = Sse2.Insert(shortVector, low, (byte)(index * 2 + 0)); + shortVector = Sse2.Insert(shortVector, high, (byte)(index * 2 + 1)); + + return Sse.StaticCast(shortVector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Sse41VectorInsertScalarSingle(float value, Vector128 vector) + { + //Note: 0b1110 is the mask to zero the upper bits. + return Sse41.Insert(vector, value, 0b1110); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSByteZero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt16Zero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt32Zero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt64Zero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleZero() + { + if (Sse.IsSupported) + { + return Sse.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorDoubleZero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToSByte(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToInt16(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToInt32(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToInt64(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToByte(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToUInt16(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToUInt32(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToUInt64(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToDouble(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSByteToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt16ToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt32ToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt64ToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorByteToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorUInt16ToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorUInt32ToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorUInt64ToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorDoubleToSingle(Vector128 vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(vector); + } + + throw new PlatformNotSupportedException(); + } + } +} diff --git a/ChocolArm64/Instruction32/A32InstInterpretAlu.cs b/ChocolArm64/Instructions32/A32InstInterpretAlu.cs similarity index 61% rename from ChocolArm64/Instruction32/A32InstInterpretAlu.cs rename to ChocolArm64/Instructions32/A32InstInterpretAlu.cs index 41b9d22aeb..f3be823ffc 100644 --- a/ChocolArm64/Instruction32/A32InstInterpretAlu.cs +++ b/ChocolArm64/Instructions32/A32InstInterpretAlu.cs @@ -1,4 +1,4 @@ -namespace ChocolArm64.Instruction32 +namespace ChocolArm64.Instructions32 { static partial class A32InstInterpret { diff --git a/ChocolArm64/Instructions32/A32InstInterpretFlow.cs b/ChocolArm64/Instructions32/A32InstInterpretFlow.cs new file mode 100644 index 0000000000..cdf7e4c6af --- /dev/null +++ b/ChocolArm64/Instructions32/A32InstInterpretFlow.cs @@ -0,0 +1,70 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Decoders32; +using ChocolArm64.Memory; +using ChocolArm64.State; + +using static ChocolArm64.Instructions32.A32InstInterpretHelper; + +namespace ChocolArm64.Instructions32 +{ + static partial class A32InstInterpret + { + public static void B(CpuThreadState state, MemoryManager memory, OpCode64 opCode) + { + A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode; + + if (IsConditionTrue(state, op.Cond)) + { + BranchWritePc(state, GetPc(state) + (uint)op.Imm); + } + } + + public static void Bl(CpuThreadState state, MemoryManager memory, OpCode64 opCode) + { + Blx(state, memory, opCode, false); + } + + public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode) + { + Blx(state, memory, opCode, true); + } + + public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode, bool x) + { + A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode; + + if (IsConditionTrue(state, op.Cond)) + { + uint pc = GetPc(state); + + if (state.Thumb) + { + state.R14 = pc | 1; + } + else + { + state.R14 = pc - 4U; + } + + if (x) + { + state.Thumb = !state.Thumb; + } + + if (!state.Thumb) + { + pc &= ~3U; + } + + BranchWritePc(state, pc + (uint)op.Imm); + } + } + + private static void BranchWritePc(CpuThreadState state, uint pc) + { + state.R15 = state.Thumb + ? pc & ~1U + : pc & ~3U; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instructions32/A32InstInterpretHelper.cs b/ChocolArm64/Instructions32/A32InstInterpretHelper.cs new file mode 100644 index 0000000000..b08e12984a --- /dev/null +++ b/ChocolArm64/Instructions32/A32InstInterpretHelper.cs @@ -0,0 +1,65 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using System; + +namespace ChocolArm64.Instructions32 +{ + static class A32InstInterpretHelper + { + public static bool IsConditionTrue(CpuThreadState state, Cond cond) + { + switch (cond) + { + case Cond.Eq: return state.Zero; + case Cond.Ne: return !state.Zero; + case Cond.GeUn: return state.Carry; + case Cond.LtUn: return !state.Carry; + case Cond.Mi: return state.Negative; + case Cond.Pl: return !state.Negative; + case Cond.Vs: return state.Overflow; + case Cond.Vc: return !state.Overflow; + case Cond.GtUn: return state.Carry && !state.Zero; + case Cond.LeUn: return !state.Carry && state.Zero; + case Cond.Ge: return state.Negative == state.Overflow; + case Cond.Lt: return state.Negative != state.Overflow; + case Cond.Gt: return state.Negative == state.Overflow && !state.Zero; + case Cond.Le: return state.Negative != state.Overflow && state.Zero; + } + + return true; + } + + public unsafe static uint GetReg(CpuThreadState state, int reg) + { + if ((uint)reg > 15) + { + throw new ArgumentOutOfRangeException(nameof(reg)); + } + + fixed (uint* ptr = &state.R0) + { + return *(ptr + reg); + } + } + + public unsafe static void SetReg(CpuThreadState state, int reg, uint value) + { + if ((uint)reg > 15) + { + throw new ArgumentOutOfRangeException(nameof(reg)); + } + + fixed (uint* ptr = &state.R0) + { + *(ptr + reg) = value; + } + } + + public static uint GetPc(CpuThreadState state) + { + //Due to the old fetch-decode-execute pipeline of old ARM CPUs, + //the PC is 4 or 8 bytes (2 instructions) ahead of the current instruction. + return state.R15 + (state.Thumb ? 2U : 4U); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs deleted file mode 100644 index 566e6b54ce..0000000000 --- a/ChocolArm64/Memory/AMemory.cs +++ /dev/null @@ -1,709 +0,0 @@ -using ChocolArm64.Exceptions; -using ChocolArm64.State; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -using System.Threading; - -namespace ChocolArm64.Memory -{ - public unsafe class AMemory : IAMemory, IDisposable - { - private const int PTLvl0Bits = 13; - private const int PTLvl1Bits = 14; - private const int PTPageBits = 12; - - private const int PTLvl0Size = 1 << PTLvl0Bits; - private const int PTLvl1Size = 1 << PTLvl1Bits; - public const int PageSize = 1 << PTPageBits; - - private const int PTLvl0Mask = PTLvl0Size - 1; - private const int PTLvl1Mask = PTLvl1Size - 1; - public const int PageMask = PageSize - 1; - - private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; - private const int PTLvl1Bit = PTPageBits; - - private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; - - private class ArmMonitor - { - public long Position; - public bool ExState; - - public bool HasExclusiveAccess(long Position) - { - return this.Position == Position && ExState; - } - } - - private Dictionary Monitors; - - private ConcurrentDictionary ObservedPages; - - public IntPtr Ram { get; private set; } - - private byte* RamPtr; - - private byte*** PageTable; - - public AMemory(IntPtr Ram) - { - Monitors = new Dictionary(); - - ObservedPages = new ConcurrentDictionary(); - - this.Ram = Ram; - - RamPtr = (byte*)Ram; - - PageTable = (byte***)Marshal.AllocHGlobal(PTLvl0Size * IntPtr.Size); - - for (int L0 = 0; L0 < PTLvl0Size; L0++) - { - PageTable[L0] = null; - } - } - - public void RemoveMonitor(AThreadState State) - { - lock (Monitors) - { - ClearExclusive(State); - - Monitors.Remove(State); - } - } - - public void SetExclusive(AThreadState ThreadState, long Position) - { - Position &= ~ErgMask; - - lock (Monitors) - { - foreach (ArmMonitor Mon in Monitors.Values) - { - if (Mon.Position == Position && Mon.ExState) - { - Mon.ExState = false; - } - } - - if (!Monitors.TryGetValue(ThreadState, out ArmMonitor ThreadMon)) - { - ThreadMon = new ArmMonitor(); - - Monitors.Add(ThreadState, ThreadMon); - } - - ThreadMon.Position = Position; - ThreadMon.ExState = true; - } - } - - public bool TestExclusive(AThreadState ThreadState, long Position) - { - //Note: Any call to this method also should be followed by a - //call to ClearExclusiveForStore if this method returns true. - Position &= ~ErgMask; - - Monitor.Enter(Monitors); - - if (!Monitors.TryGetValue(ThreadState, out ArmMonitor ThreadMon)) - { - return false; - } - - bool ExState = ThreadMon.HasExclusiveAccess(Position); - - if (!ExState) - { - Monitor.Exit(Monitors); - } - - return ExState; - } - - public void ClearExclusiveForStore(AThreadState ThreadState) - { - if (Monitors.TryGetValue(ThreadState, out ArmMonitor ThreadMon)) - { - ThreadMon.ExState = false; - } - - Monitor.Exit(Monitors); - } - - public void ClearExclusive(AThreadState ThreadState) - { - lock (Monitors) - { - if (Monitors.TryGetValue(ThreadState, out ArmMonitor ThreadMon)) - { - ThreadMon.ExState = false; - } - } - } - - public void WriteInt32ToSharedAddr(long Position, int Value) - { - long MaskedPosition = Position & ~ErgMask; - - lock (Monitors) - { - foreach (ArmMonitor Mon in Monitors.Values) - { - if (Mon.Position == MaskedPosition && Mon.ExState) - { - Mon.ExState = false; - } - } - - WriteInt32(Position, Value); - } - } - - public sbyte ReadSByte(long Position) - { - return (sbyte)ReadByte(Position); - } - - public short ReadInt16(long Position) - { - return (short)ReadUInt16(Position); - } - - public int ReadInt32(long Position) - { - return (int)ReadUInt32(Position); - } - - public long ReadInt64(long Position) - { - return (long)ReadUInt64(Position); - } - - public byte ReadByte(long Position) - { - return *((byte*)Translate(Position)); - } - - public ushort ReadUInt16(long Position) - { - return *((ushort*)Translate(Position)); - } - - public uint ReadUInt32(long Position) - { - return *((uint*)Translate(Position)); - } - - public ulong ReadUInt64(long Position) - { - return *((ulong*)Translate(Position)); - } - - public Vector128 ReadVector8(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ReadByte(Position))); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector128 ReadVector16(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.Insert(Sse2.SetZeroVector128(), ReadUInt16(Position), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public Vector128 ReadVector32(long Position) - { - if (Sse.IsSupported) - { - return Sse.LoadScalarVector128((float*)Translate(Position)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public Vector128 ReadVector64(long Position) - { - if (Sse2.IsSupported) - { - return Sse.StaticCast(Sse2.LoadScalarVector128((double*)Translate(Position))); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector128 ReadVector128(long Position) - { - if (Sse.IsSupported) - { - return Sse.LoadVector128((float*)Translate(Position)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - public byte[] ReadBytes(long Position, long Size) - { - if ((uint)Size > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - EnsureRangeIsValid(Position, Size); - - byte[] Data = new byte[Size]; - - Marshal.Copy((IntPtr)Translate(Position), Data, 0, (int)Size); - - return Data; - } - - public void WriteSByte(long Position, sbyte Value) - { - WriteByte(Position, (byte)Value); - } - - public void WriteInt16(long Position, short Value) - { - WriteUInt16(Position, (ushort)Value); - } - - public void WriteInt32(long Position, int Value) - { - WriteUInt32(Position, (uint)Value); - } - - public void WriteInt64(long Position, long Value) - { - WriteUInt64(Position, (ulong)Value); - } - - public void WriteByte(long Position, byte Value) - { - *((byte*)TranslateWrite(Position)) = Value; - } - - public void WriteUInt16(long Position, ushort Value) - { - *((ushort*)TranslateWrite(Position)) = Value; - } - - public void WriteUInt32(long Position, uint Value) - { - *((uint*)TranslateWrite(Position)) = Value; - } - - public void WriteUInt64(long Position, ulong Value) - { - *((ulong*)TranslateWrite(Position)) = Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector8(long Position, Vector128 Value) - { - if (Sse41.IsSupported) - { - WriteByte(Position, Sse41.Extract(Sse.StaticCast(Value), 0)); - } - else if (Sse2.IsSupported) - { - WriteByte(Position, (byte)Sse2.Extract(Sse.StaticCast(Value), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector16(long Position, Vector128 Value) - { - if (Sse2.IsSupported) - { - WriteUInt16(Position, Sse2.Extract(Sse.StaticCast(Value), 0)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public void WriteVector32(long Position, Vector128 Value) - { - if (Sse.IsSupported) - { - Sse.StoreScalar((float*)TranslateWrite(Position), Value); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public void WriteVector64(long Position, Vector128 Value) - { - if (Sse2.IsSupported) - { - Sse2.StoreScalar((double*)TranslateWrite(Position), Sse.StaticCast(Value)); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteVector128(long Position, Vector128 Value) - { - if (Sse.IsSupported) - { - Sse.Store((float*)TranslateWrite(Position), Value); - } - else - { - throw new PlatformNotSupportedException(); - } - } - - public void WriteBytes(long Position, byte[] Data) - { - EnsureRangeIsValid(Position, (uint)Data.Length); - - Marshal.Copy(Data, 0, (IntPtr)TranslateWrite(Position), Data.Length); - } - - public void Map(long VA, long PA, long Size) - { - SetPTEntries(VA, RamPtr + PA, Size); - } - - public void Unmap(long Position, long Size) - { - SetPTEntries(Position, null, Size); - - StopObservingRegion(Position, Size); - } - - public bool IsMapped(long Position) - { - if (!(IsValidPosition(Position))) - { - return false; - } - - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - return false; - } - - return PageTable[L0][L1] != null || ObservedPages.ContainsKey(Position >> PTPageBits); - } - - public long GetPhysicalAddress(long VirtualAddress) - { - byte* Ptr = Translate(VirtualAddress); - - return (long)(Ptr - RamPtr); - } - - internal byte* Translate(long Position) - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - long Old = Position; - - byte** Lvl1 = PageTable[L0]; - - if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0) - { - goto Unmapped; - } - - if (Lvl1 == null) - { - goto Unmapped; - } - - Position &= PageMask; - - byte* Ptr = Lvl1[L1]; - - if (Ptr == null) - { - goto Unmapped; - } - - return Ptr + Position; - -Unmapped: - return HandleNullPte(Old); - } - - private byte* HandleNullPte(long Position) - { - long Key = Position >> PTPageBits; - - if (ObservedPages.TryGetValue(Key, out IntPtr Ptr)) - { - return (byte*)Ptr + (Position & PageMask); - } - - throw new VmmPageFaultException(Position); - } - - internal byte* TranslateWrite(long Position) - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - long Old = Position; - - byte** Lvl1 = PageTable[L0]; - - if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0) - { - goto Unmapped; - } - - if (Lvl1 == null) - { - goto Unmapped; - } - - Position &= PageMask; - - byte* Ptr = Lvl1[L1]; - - if (Ptr == null) - { - goto Unmapped; - } - - return Ptr + Position; - -Unmapped: - return HandleNullPteWrite(Old); - } - - private byte* HandleNullPteWrite(long Position) - { - long Key = Position >> PTPageBits; - - if (ObservedPages.TryGetValue(Key, out IntPtr Ptr)) - { - SetPTEntry(Position, (byte*)Ptr); - - return (byte*)Ptr + (Position & PageMask); - } - - throw new VmmPageFaultException(Position); - } - - private void SetPTEntries(long VA, byte* Ptr, long Size) - { - long EndPosition = (VA + Size + PageMask) & ~PageMask; - - while ((ulong)VA < (ulong)EndPosition) - { - SetPTEntry(VA, Ptr); - - VA += PageSize; - - if (Ptr != null) - { - Ptr += PageSize; - } - } - } - - private void SetPTEntry(long Position, byte* Ptr) - { - if (!IsValidPosition(Position)) - { - throw new ArgumentOutOfRangeException(nameof(Position)); - } - - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - if (PageTable[L0] == null) - { - byte** Lvl1 = (byte**)Marshal.AllocHGlobal(PTLvl1Size * IntPtr.Size); - - for (int ZL1 = 0; ZL1 < PTLvl1Size; ZL1++) - { - Lvl1[ZL1] = null; - } - - Thread.MemoryBarrier(); - - PageTable[L0] = Lvl1; - } - - PageTable[L0][L1] = Ptr; - } - - public (bool[], int) IsRegionModified(long Position, long Size) - { - long EndPosition = (Position + Size + PageMask) & ~PageMask; - - Position &= ~PageMask; - - Size = EndPosition - Position; - - bool[] Modified = new bool[Size >> PTPageBits]; - - int Count = 0; - - lock (ObservedPages) - { - for (int Page = 0; Page < Modified.Length; Page++) - { - byte* Ptr = Translate(Position); - - if (ObservedPages.TryAdd(Position >> PTPageBits, (IntPtr)Ptr)) - { - Modified[Page] = true; - - Count++; - } - else - { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; - - byte** Lvl1 = PageTable[L0]; - - if (Lvl1 != null) - { - if (Modified[Page] = Lvl1[L1] != null) - { - Count++; - } - } - } - - SetPTEntry(Position, null); - - Position += PageSize; - } - } - - return (Modified, Count); - } - - public void StopObservingRegion(long Position, long Size) - { - long EndPosition = (Position + Size + PageMask) & ~PageMask; - - while (Position < EndPosition) - { - lock (ObservedPages) - { - if (ObservedPages.TryRemove(Position >> PTPageBits, out IntPtr Ptr)) - { - SetPTEntry(Position, (byte*)Ptr); - } - } - - Position += PageSize; - } - } - - public IntPtr GetHostAddress(long Position, long Size) - { - EnsureRangeIsValid(Position, Size); - - return (IntPtr)Translate(Position); - } - - internal void EnsureRangeIsValid(long Position, long Size) - { - long EndPos = Position + Size; - - Position &= ~PageMask; - - long ExpectedPA = GetPhysicalAddress(Position); - - while ((ulong)Position < (ulong)EndPos) - { - long PA = GetPhysicalAddress(Position); - - if (PA != ExpectedPA) - { - throw new VmmAccessException(Position, Size); - } - - Position += PageSize; - ExpectedPA += PageSize; - } - } - - public bool IsValidPosition(long Position) - { - return Position >> (PTLvl0Bits + PTLvl1Bits + PTPageBits) == 0; - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (PageTable == null) - { - return; - } - - for (int L0 = 0; L0 < PTLvl0Size; L0++) - { - if (PageTable[L0] != null) - { - Marshal.FreeHGlobal((IntPtr)PageTable[L0]); - } - - PageTable[L0] = null; - } - - Marshal.FreeHGlobal((IntPtr)PageTable); - - PageTable = null; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Memory/AMemoryHelper.cs b/ChocolArm64/Memory/AMemoryHelper.cs deleted file mode 100644 index ea87783438..0000000000 --- a/ChocolArm64/Memory/AMemoryHelper.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; - -namespace ChocolArm64.Memory -{ - public static class AMemoryHelper - { - public static void FillWithZeros(AMemory Memory, long Position, int Size) - { - int Size8 = Size & ~(8 - 1); - - for (int Offs = 0; Offs < Size8; Offs += 8) - { - Memory.WriteInt64(Position + Offs, 0); - } - - for (int Offs = Size8; Offs < (Size - Size8); Offs++) - { - Memory.WriteByte(Position + Offs, 0); - } - } - - public unsafe static T Read(AMemory Memory, long Position) where T : struct - { - long Size = Marshal.SizeOf(); - - Memory.EnsureRangeIsValid(Position, Size); - - IntPtr Ptr = (IntPtr)Memory.Translate(Position); - - return Marshal.PtrToStructure(Ptr); - } - - public unsafe static void Write(AMemory Memory, long Position, T Value) where T : struct - { - long Size = Marshal.SizeOf(); - - Memory.EnsureRangeIsValid(Position, Size); - - IntPtr Ptr = (IntPtr)Memory.TranslateWrite(Position); - - Marshal.StructureToPtr(Value, Ptr, false); - } - - public static string ReadAsciiString(AMemory Memory, long Position, long MaxSize = -1) - { - using (MemoryStream MS = new MemoryStream()) - { - for (long Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++) - { - byte Value = (byte)Memory.ReadByte(Position + Offs); - - if (Value == 0) - { - break; - } - - MS.WriteByte(Value); - } - - return Encoding.ASCII.GetString(MS.ToArray()); - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Memory/IAMemory.cs b/ChocolArm64/Memory/IAMemory.cs deleted file mode 100644 index 5b7d17bb8b..0000000000 --- a/ChocolArm64/Memory/IAMemory.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace ChocolArm64.Memory -{ - public interface IAMemory - { - sbyte ReadSByte(long Position); - - short ReadInt16(long Position); - - int ReadInt32(long Position); - - long ReadInt64(long Position); - - byte ReadByte(long Position); - - ushort ReadUInt16(long Position); - - uint ReadUInt32(long Position); - - ulong ReadUInt64(long Position); - - void WriteSByte(long Position, sbyte Value); - - void WriteInt16(long Position, short Value); - - void WriteInt32(long Position, int Value); - - void WriteInt64(long Position, long Value); - - void WriteByte(long Position, byte Value); - - void WriteUInt16(long Position, ushort Value); - - void WriteUInt32(long Position, uint Value); - - void WriteUInt64(long Position, ulong Value); - } -} \ No newline at end of file diff --git a/ChocolArm64/Memory/IMemory.cs b/ChocolArm64/Memory/IMemory.cs new file mode 100644 index 0000000000..dc582155f8 --- /dev/null +++ b/ChocolArm64/Memory/IMemory.cs @@ -0,0 +1,37 @@ +namespace ChocolArm64.Memory +{ + public interface IMemory + { + sbyte ReadSByte(long position); + + short ReadInt16(long position); + + int ReadInt32(long position); + + long ReadInt64(long position); + + byte ReadByte(long position); + + ushort ReadUInt16(long position); + + uint ReadUInt32(long position); + + ulong ReadUInt64(long position); + + void WriteSByte(long position, sbyte value); + + void WriteInt16(long position, short value); + + void WriteInt32(long position, int value); + + void WriteInt64(long position, long value); + + void WriteByte(long position, byte value); + + void WriteUInt16(long position, ushort value); + + void WriteUInt32(long position, uint value); + + void WriteUInt64(long position, ulong value); + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryHelper.cs b/ChocolArm64/Memory/MemoryHelper.cs new file mode 100644 index 0000000000..2e721afa1c --- /dev/null +++ b/ChocolArm64/Memory/MemoryHelper.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace ChocolArm64.Memory +{ + public static class MemoryHelper + { + public static void FillWithZeros(MemoryManager memory, long position, int size) + { + int size8 = size & ~(8 - 1); + + for (int offs = 0; offs < size8; offs += 8) + { + memory.WriteInt64(position + offs, 0); + } + + for (int offs = size8; offs < (size - size8); offs++) + { + memory.WriteByte(position + offs, 0); + } + } + + public unsafe static T Read(MemoryManager memory, long position) where T : struct + { + long size = Marshal.SizeOf(); + + memory.EnsureRangeIsValid(position, size); + + IntPtr ptr = (IntPtr)memory.Translate(position); + + return Marshal.PtrToStructure(ptr); + } + + public unsafe static void Write(MemoryManager memory, long position, T value) where T : struct + { + long size = Marshal.SizeOf(); + + memory.EnsureRangeIsValid(position, size); + + IntPtr ptr = (IntPtr)memory.TranslateWrite(position); + + Marshal.StructureToPtr(value, ptr, false); + } + + public static string ReadAsciiString(MemoryManager memory, long position, long maxSize = -1) + { + using (MemoryStream ms = new MemoryStream()) + { + for (long offs = 0; offs < maxSize || maxSize == -1; offs++) + { + byte value = (byte)memory.ReadByte(position + offs); + + if (value == 0) + { + break; + } + + ms.WriteByte(value); + } + + return Encoding.ASCII.GetString(ms.ToArray()); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryManager.cs b/ChocolArm64/Memory/MemoryManager.cs new file mode 100644 index 0000000000..ef3fb00646 --- /dev/null +++ b/ChocolArm64/Memory/MemoryManager.cs @@ -0,0 +1,767 @@ +using ChocolArm64.Events; +using ChocolArm64.Exceptions; +using ChocolArm64.State; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using System.Threading; + +namespace ChocolArm64.Memory +{ + public unsafe class MemoryManager : IMemory, IDisposable + { + private const int PtLvl0Bits = 13; + private const int PtLvl1Bits = 14; + private const int PtPageBits = 12; + + private const int PtLvl0Size = 1 << PtLvl0Bits; + private const int PtLvl1Size = 1 << PtLvl1Bits; + public const int PageSize = 1 << PtPageBits; + + private const int PtLvl0Mask = PtLvl0Size - 1; + private const int PtLvl1Mask = PtLvl1Size - 1; + public const int PageMask = PageSize - 1; + + private const int PtLvl0Bit = PtPageBits + PtLvl1Bits; + private const int PtLvl1Bit = PtPageBits; + + private const long ErgMask = (4 << CpuThreadState.ErgSizeLog2) - 1; + + private class ArmMonitor + { + public long Position; + public bool ExState; + + public bool HasExclusiveAccess(long position) + { + return Position == position && ExState; + } + } + + private Dictionary _monitors; + + private ConcurrentDictionary _observedPages; + + public IntPtr Ram { get; private set; } + + private byte* _ramPtr; + + private byte*** _pageTable; + + public event EventHandler InvalidAccess; + + public MemoryManager(IntPtr ram) + { + _monitors = new Dictionary(); + + _observedPages = new ConcurrentDictionary(); + + Ram = ram; + + _ramPtr = (byte*)ram; + + _pageTable = (byte***)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size); + + for (int l0 = 0; l0 < PtLvl0Size; l0++) + { + _pageTable[l0] = null; + } + } + + public void RemoveMonitor(int core) + { + lock (_monitors) + { + ClearExclusive(core); + + _monitors.Remove(core); + } + } + + public void SetExclusive(int core, long position) + { + position &= ~ErgMask; + + lock (_monitors) + { + foreach (ArmMonitor mon in _monitors.Values) + { + if (mon.Position == position && mon.ExState) + { + mon.ExState = false; + } + } + + if (!_monitors.TryGetValue(core, out ArmMonitor threadMon)) + { + threadMon = new ArmMonitor(); + + _monitors.Add(core, threadMon); + } + + threadMon.Position = position; + threadMon.ExState = true; + } + } + + public bool TestExclusive(int core, long position) + { + //Note: Any call to this method also should be followed by a + //call to ClearExclusiveForStore if this method returns true. + position &= ~ErgMask; + + Monitor.Enter(_monitors); + + if (!_monitors.TryGetValue(core, out ArmMonitor threadMon)) + { + return false; + } + + bool exState = threadMon.HasExclusiveAccess(position); + + if (!exState) + { + Monitor.Exit(_monitors); + } + + return exState; + } + + public void ClearExclusiveForStore(int core) + { + if (_monitors.TryGetValue(core, out ArmMonitor threadMon)) + { + threadMon.ExState = false; + } + + Monitor.Exit(_monitors); + } + + public void ClearExclusive(int core) + { + lock (_monitors) + { + if (_monitors.TryGetValue(core, out ArmMonitor threadMon)) + { + threadMon.ExState = false; + } + } + } + + public void WriteInt32ToSharedAddr(long position, int value) + { + long maskedPosition = position & ~ErgMask; + + lock (_monitors) + { + foreach (ArmMonitor mon in _monitors.Values) + { + if (mon.Position == maskedPosition && mon.ExState) + { + mon.ExState = false; + } + } + + WriteInt32(position, value); + } + } + + public sbyte ReadSByte(long position) + { + return (sbyte)ReadByte(position); + } + + public short ReadInt16(long position) + { + return (short)ReadUInt16(position); + } + + public int ReadInt32(long position) + { + return (int)ReadUInt32(position); + } + + public long ReadInt64(long position) + { + return (long)ReadUInt64(position); + } + + public byte ReadByte(long position) + { + return *((byte*)Translate(position)); + } + + public ushort ReadUInt16(long position) + { + return *((ushort*)Translate(position)); + } + + public uint ReadUInt32(long position) + { + return *((uint*)Translate(position)); + } + + public ulong ReadUInt64(long position) + { + return *((ulong*)Translate(position)); + } + + public Vector128 ReadVector8(long position) + { + if (Sse2.IsSupported) + { + return Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ReadByte(position))); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 ReadVector16(long position) + { + if (Sse2.IsSupported) + { + return Sse.StaticCast(Sse2.Insert(Sse2.SetZeroVector128(), ReadUInt16(position), 0)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 ReadVector32(long position) + { + if (Sse.IsSupported) + { + return Sse.LoadScalarVector128((float*)Translate(position)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 ReadVector64(long position) + { + if (Sse2.IsSupported) + { + return Sse.StaticCast(Sse2.LoadScalarVector128((double*)Translate(position))); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector128 ReadVector128(long position) + { + if (Sse.IsSupported) + { + return Sse.LoadVector128((float*)Translate(position)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public byte[] ReadBytes(long position, long size) + { + if ((uint)size > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + EnsureRangeIsValid(position, size); + + byte[] data = new byte[size]; + + Marshal.Copy((IntPtr)Translate(position), data, 0, (int)size); + + return data; + } + + public void ReadBytes(long position, byte[] data, int startIndex, int size) + { + //Note: This will be moved later. + EnsureRangeIsValid(position, (uint)size); + + Marshal.Copy((IntPtr)Translate(position), data, startIndex, size); + } + + public void WriteSByte(long position, sbyte value) + { + WriteByte(position, (byte)value); + } + + public void WriteInt16(long position, short value) + { + WriteUInt16(position, (ushort)value); + } + + public void WriteInt32(long position, int value) + { + WriteUInt32(position, (uint)value); + } + + public void WriteInt64(long position, long value) + { + WriteUInt64(position, (ulong)value); + } + + public void WriteByte(long position, byte value) + { + *((byte*)TranslateWrite(position)) = value; + } + + public void WriteUInt16(long position, ushort value) + { + *((ushort*)TranslateWrite(position)) = value; + } + + public void WriteUInt32(long position, uint value) + { + *((uint*)TranslateWrite(position)) = value; + } + + public void WriteUInt64(long position, ulong value) + { + *((ulong*)TranslateWrite(position)) = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector8(long position, Vector128 value) + { + if (Sse41.IsSupported) + { + WriteByte(position, Sse41.Extract(Sse.StaticCast(value), 0)); + } + else if (Sse2.IsSupported) + { + WriteByte(position, (byte)Sse2.Extract(Sse.StaticCast(value), 0)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector16(long position, Vector128 value) + { + if (Sse2.IsSupported) + { + WriteUInt16(position, Sse2.Extract(Sse.StaticCast(value), 0)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector32(long position, Vector128 value) + { + if (Sse.IsSupported) + { + Sse.StoreScalar((float*)TranslateWrite(position), value); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector64(long position, Vector128 value) + { + if (Sse2.IsSupported) + { + Sse2.StoreScalar((double*)TranslateWrite(position), Sse.StaticCast(value)); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteVector128(long position, Vector128 value) + { + if (Sse.IsSupported) + { + Sse.Store((float*)TranslateWrite(position), value); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public void WriteBytes(long position, byte[] data) + { + long endAddr = position + data.Length; + + if ((ulong)endAddr < (ulong)position) + { + throw new ArgumentOutOfRangeException(nameof(position)); + } + + int offset = 0; + + while ((ulong)position < (ulong)endAddr) + { + long pageLimit = (position + PageSize) & ~(long)PageMask; + + if ((ulong)pageLimit > (ulong)endAddr) + { + pageLimit = endAddr; + } + + int copySize = (int)(pageLimit - position); + + Marshal.Copy(data, offset, (IntPtr)TranslateWrite(position), copySize); + + position += copySize; + offset += copySize; + } + } + + public void WriteBytes(long position, byte[] data, int startIndex, int size) + { + //Note: This will be moved later. + //Using Translate instead of TranslateWrite is on purpose. + EnsureRangeIsValid(position, (uint)size); + + Marshal.Copy(data, startIndex, (IntPtr)Translate(position), size); + } + + public void CopyBytes(long src, long dst, long size) + { + //Note: This will be moved later. + EnsureRangeIsValid(src, size); + EnsureRangeIsValid(dst, size); + + byte* srcPtr = Translate(src); + byte* dstPtr = TranslateWrite(dst); + + Buffer.MemoryCopy(srcPtr, dstPtr, size, size); + } + + public void Map(long va, long pa, long size) + { + SetPtEntries(va, _ramPtr + pa, size); + } + + public void Unmap(long position, long size) + { + SetPtEntries(position, null, size); + + StopObservingRegion(position, size); + } + + public bool IsMapped(long position) + { + if (!(IsValidPosition(position))) + { + return false; + } + + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + if (_pageTable[l0] == null) + { + return false; + } + + return _pageTable[l0][l1] != null || _observedPages.ContainsKey(position >> PtPageBits); + } + + public long GetPhysicalAddress(long virtualAddress) + { + byte* ptr = Translate(virtualAddress); + + return (long)(ptr - _ramPtr); + } + + internal byte* Translate(long position) + { + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + long old = position; + + byte** lvl1 = _pageTable[l0]; + + if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) + { + goto Unmapped; + } + + if (lvl1 == null) + { + goto Unmapped; + } + + position &= PageMask; + + byte* ptr = lvl1[l1]; + + if (ptr == null) + { + goto Unmapped; + } + + return ptr + position; + +Unmapped: + return HandleNullPte(old); + } + + private byte* HandleNullPte(long position) + { + long key = position >> PtPageBits; + + if (_observedPages.TryGetValue(key, out IntPtr ptr)) + { + return (byte*)ptr + (position & PageMask); + } + + InvalidAccess?.Invoke(this, new InvalidAccessEventArgs(position)); + + throw new VmmPageFaultException(position); + } + + internal byte* TranslateWrite(long position) + { + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + long old = position; + + byte** lvl1 = _pageTable[l0]; + + if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) + { + goto Unmapped; + } + + if (lvl1 == null) + { + goto Unmapped; + } + + position &= PageMask; + + byte* ptr = lvl1[l1]; + + if (ptr == null) + { + goto Unmapped; + } + + return ptr + position; + +Unmapped: + return HandleNullPteWrite(old); + } + + private byte* HandleNullPteWrite(long position) + { + long key = position >> PtPageBits; + + if (_observedPages.TryGetValue(key, out IntPtr ptr)) + { + SetPtEntry(position, (byte*)ptr); + + return (byte*)ptr + (position & PageMask); + } + + InvalidAccess?.Invoke(this, new InvalidAccessEventArgs(position)); + + throw new VmmPageFaultException(position); + } + + private void SetPtEntries(long va, byte* ptr, long size) + { + long endPosition = (va + size + PageMask) & ~PageMask; + + while ((ulong)va < (ulong)endPosition) + { + SetPtEntry(va, ptr); + + va += PageSize; + + if (ptr != null) + { + ptr += PageSize; + } + } + } + + private void SetPtEntry(long position, byte* ptr) + { + if (!IsValidPosition(position)) + { + throw new ArgumentOutOfRangeException(nameof(position)); + } + + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + if (_pageTable[l0] == null) + { + byte** lvl1 = (byte**)Marshal.AllocHGlobal(PtLvl1Size * IntPtr.Size); + + for (int zl1 = 0; zl1 < PtLvl1Size; zl1++) + { + lvl1[zl1] = null; + } + + Thread.MemoryBarrier(); + + _pageTable[l0] = lvl1; + } + + _pageTable[l0][l1] = ptr; + } + + public (bool[], int) IsRegionModified(long position, long size) + { + long endPosition = (position + size + PageMask) & ~PageMask; + + position &= ~PageMask; + + size = endPosition - position; + + bool[] modified = new bool[size >> PtPageBits]; + + int count = 0; + + lock (_observedPages) + { + for (int page = 0; page < modified.Length; page++) + { + byte* ptr = Translate(position); + + if (_observedPages.TryAdd(position >> PtPageBits, (IntPtr)ptr)) + { + modified[page] = true; + + count++; + } + else + { + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; + + byte** lvl1 = _pageTable[l0]; + + if (lvl1 != null) + { + if (modified[page] = lvl1[l1] != null) + { + count++; + } + } + } + + SetPtEntry(position, null); + + position += PageSize; + } + } + + return (modified, count); + } + + public void StopObservingRegion(long position, long size) + { + long endPosition = (position + size + PageMask) & ~PageMask; + + while (position < endPosition) + { + lock (_observedPages) + { + if (_observedPages.TryRemove(position >> PtPageBits, out IntPtr ptr)) + { + SetPtEntry(position, (byte*)ptr); + } + } + + position += PageSize; + } + } + + public IntPtr GetHostAddress(long position, long size) + { + EnsureRangeIsValid(position, size); + + return (IntPtr)Translate(position); + } + + internal void EnsureRangeIsValid(long position, long size) + { + long endPos = position + size; + + position &= ~PageMask; + + long expectedPa = GetPhysicalAddress(position); + + while ((ulong)position < (ulong)endPos) + { + long pa = GetPhysicalAddress(position); + + if (pa != expectedPa) + { + throw new VmmAccessException(position, size); + } + + position += PageSize; + expectedPa += PageSize; + } + } + + public bool IsValidPosition(long position) + { + return position >> (PtLvl0Bits + PtLvl1Bits + PtPageBits) == 0; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (_pageTable == null) + { + return; + } + + for (int l0 = 0; l0 < PtLvl0Size; l0++) + { + if (_pageTable[l0] != null) + { + Marshal.FreeHGlobal((IntPtr)_pageTable[l0]); + } + + _pageTable[l0] = null; + } + + Marshal.FreeHGlobal((IntPtr)_pageTable); + + _pageTable = null; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/OpCodeTable.cs b/ChocolArm64/OpCodeTable.cs new file mode 100644 index 0000000000..8151718f36 --- /dev/null +++ b/ChocolArm64/OpCodeTable.cs @@ -0,0 +1,713 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Decoders32; +using ChocolArm64.Instructions; +using ChocolArm64.Instructions32; +using ChocolArm64.State; +using System; +using System.Collections.Generic; + +namespace ChocolArm64 +{ + static class OpCodeTable + { + static OpCodeTable() + { +#region "OpCode Table (AArch32)" + //Integer + SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.B, typeof(A32OpCodeBImmAl)); + SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl, typeof(A32OpCodeBImmAl)); + SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx, typeof(A32OpCodeBImmAl)); +#endregion + +#region "OpCode Table (AArch64)" + //Integer + SetA64("x0011010000xxxxx000000xxxxxxxxxx", InstEmit.Adc, typeof(OpCodeAluRs64)); + SetA64("x0111010000xxxxx000000xxxxxxxxxx", InstEmit.Adcs, typeof(OpCodeAluRs64)); + SetA64("x00100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluImm64)); + SetA64("00001011<<0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluRs64)); + SetA64("10001011<<0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluRs64)); + SetA64("x0001011001xxxxxxxx0xxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluRx64)); + SetA64("x0001011001xxxxxxxx100xxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluRx64)); + SetA64("x01100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluImm64)); + SetA64("00101011<<0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluRs64)); + SetA64("10101011<<0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluRs64)); + SetA64("x0101011001xxxxxxxx0xxxxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluRx64)); + SetA64("x0101011001xxxxxxxx100xxxxxxxxxx", InstEmit.Adds, typeof(OpCodeAluRx64)); + SetA64("0xx10000xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Adr, typeof(OpCodeAdr64)); + SetA64("1xx10000xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Adrp, typeof(OpCodeAdr64)); + SetA64("0001001000xxxxxxxxxxxxxxxxxxxxxx", InstEmit.And, typeof(OpCodeAluImm64)); + SetA64("100100100xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.And, typeof(OpCodeAluImm64)); + SetA64("00001010xx0xxxxx0xxxxxxxxxxxxxxx", InstEmit.And, typeof(OpCodeAluRs64)); + SetA64("10001010xx0xxxxxxxxxxxxxxxxxxxxx", InstEmit.And, typeof(OpCodeAluRs64)); + SetA64("0111001000xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ands, typeof(OpCodeAluImm64)); + SetA64("111100100xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ands, typeof(OpCodeAluImm64)); + SetA64("01101010xx0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Ands, typeof(OpCodeAluRs64)); + SetA64("11101010xx0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ands, typeof(OpCodeAluRs64)); + SetA64("x0011010110xxxxx001010xxxxxxxxxx", InstEmit.Asrv, typeof(OpCodeAluRs64)); + SetA64("000101xxxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.B, typeof(OpCodeBImmAl64)); + SetA64("01010100xxxxxxxxxxxxxxxxxxx0xxxx", InstEmit.B_Cond, typeof(OpCodeBImmCond64)); + SetA64("00110011000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Bfm, typeof(OpCodeBfm64)); + SetA64("1011001101xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Bfm, typeof(OpCodeBfm64)); + SetA64("00001010xx1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Bic, typeof(OpCodeAluRs64)); + SetA64("10001010xx1xxxxxxxxxxxxxxxxxxxxx", InstEmit.Bic, typeof(OpCodeAluRs64)); + SetA64("01101010xx1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Bics, typeof(OpCodeAluRs64)); + SetA64("11101010xx1xxxxxxxxxxxxxxxxxxxxx", InstEmit.Bics, typeof(OpCodeAluRs64)); + SetA64("100101xxxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Bl, typeof(OpCodeBImmAl64)); + SetA64("1101011000111111000000xxxxx00000", InstEmit.Blr, typeof(OpCodeBReg64)); + SetA64("1101011000011111000000xxxxx00000", InstEmit.Br, typeof(OpCodeBReg64)); + SetA64("11010100001xxxxxxxxxxxxxxxx00000", InstEmit.Brk, typeof(OpCodeException64)); + SetA64("x0110101xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Cbnz, typeof(OpCodeBImmCmp64)); + SetA64("x0110100xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Cbz, typeof(OpCodeBImmCmp64)); + SetA64("x0111010010xxxxxxxxx10xxxxx0xxxx", InstEmit.Ccmn, typeof(OpCodeCcmpImm64)); + SetA64("x0111010010xxxxxxxxx00xxxxx0xxxx", InstEmit.Ccmn, typeof(OpCodeCcmpReg64)); + SetA64("x1111010010xxxxxxxxx10xxxxx0xxxx", InstEmit.Ccmp, typeof(OpCodeCcmpImm64)); + SetA64("x1111010010xxxxxxxxx00xxxxx0xxxx", InstEmit.Ccmp, typeof(OpCodeCcmpReg64)); + SetA64("11010101000000110011xxxx01011111", InstEmit.Clrex, typeof(OpCodeSystem64)); + SetA64("x101101011000000000101xxxxxxxxxx", InstEmit.Cls, typeof(OpCodeAlu64)); + SetA64("x101101011000000000100xxxxxxxxxx", InstEmit.Clz, typeof(OpCodeAlu64)); + SetA64("00011010110xxxxx010000xxxxxxxxxx", InstEmit.Crc32b, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010001xxxxxxxxxx", InstEmit.Crc32h, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010010xxxxxxxxxx", InstEmit.Crc32w, typeof(OpCodeAluRs64)); + SetA64("10011010110xxxxx010011xxxxxxxxxx", InstEmit.Crc32x, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010100xxxxxxxxxx", InstEmit.Crc32cb, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010101xxxxxxxxxx", InstEmit.Crc32ch, typeof(OpCodeAluRs64)); + SetA64("00011010110xxxxx010110xxxxxxxxxx", InstEmit.Crc32cw, typeof(OpCodeAluRs64)); + SetA64("10011010110xxxxx010111xxxxxxxxxx", InstEmit.Crc32cx, typeof(OpCodeAluRs64)); + SetA64("x0011010100xxxxxxxxx00xxxxxxxxxx", InstEmit.Csel, typeof(OpCodeCsel64)); + SetA64("x0011010100xxxxxxxxx01xxxxxxxxxx", InstEmit.Csinc, typeof(OpCodeCsel64)); + SetA64("x1011010100xxxxxxxxx00xxxxxxxxxx", InstEmit.Csinv, typeof(OpCodeCsel64)); + SetA64("x1011010100xxxxxxxxx01xxxxxxxxxx", InstEmit.Csneg, typeof(OpCodeCsel64)); + SetA64("11010101000000110011xxxx10111111", InstEmit.Dmb, typeof(OpCodeSystem64)); + SetA64("11010101000000110011xxxx10011111", InstEmit.Dsb, typeof(OpCodeSystem64)); + SetA64("01001010xx1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Eon, typeof(OpCodeAluRs64)); + SetA64("11001010xx1xxxxxxxxxxxxxxxxxxxxx", InstEmit.Eon, typeof(OpCodeAluRs64)); + SetA64("0101001000xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Eor, typeof(OpCodeAluImm64)); + SetA64("110100100xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Eor, typeof(OpCodeAluImm64)); + SetA64("01001010xx0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Eor, typeof(OpCodeAluRs64)); + SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Eor, typeof(OpCodeAluRs64)); + SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstEmit.Extr, typeof(OpCodeAluRs64)); + SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstEmit.Extr, typeof(OpCodeAluRs64)); + SetA64("11010101000000110010xxxxxxx11111", InstEmit.Hint, typeof(OpCodeSystem64)); + SetA64("11010101000000110011xxxx11011111", InstEmit.Isb, typeof(OpCodeSystem64)); + SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstEmit.Ldar, typeof(OpCodeMemEx64)); + SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstEmit.Ldaxp, typeof(OpCodeMemEx64)); + SetA64("xx001000010xxxxx1xxxxxxxxxxxxxxx", InstEmit.Ldaxr, typeof(OpCodeMemEx64)); + SetA64("<<10100xx1xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldp, typeof(OpCodeMemPair64)); + SetA64("xx111000010xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeMemImm64)); + SetA64("xx11100101xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeMemImm64)); + SetA64("xx111000011xxxxxxxxx10xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeMemReg64)); + SetA64("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.LdrLit, typeof(OpCodeMemLit64)); + SetA64("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemImm64)); + SetA64("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemImm64)); + SetA64("10111000100xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemImm64)); + SetA64("1011100110xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemImm64)); + SetA64("0x1110001x1xxxxxxxxx10xxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemReg64)); + SetA64("10111000101xxxxxxxxx10xxxxxxxxxx", InstEmit.Ldrs, typeof(OpCodeMemReg64)); + SetA64("xx001000010xxxxx0xxxxxxxxxxxxxxx", InstEmit.Ldxr, typeof(OpCodeMemEx64)); + SetA64("1x001000011xxxxx0xxxxxxxxxxxxxxx", InstEmit.Ldxp, typeof(OpCodeMemEx64)); + SetA64("x0011010110xxxxx001000xxxxxxxxxx", InstEmit.Lslv, typeof(OpCodeAluRs64)); + SetA64("x0011010110xxxxx001001xxxxxxxxxx", InstEmit.Lsrv, typeof(OpCodeAluRs64)); + SetA64("x0011011000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Madd, typeof(OpCodeMul64)); + SetA64("0111001010xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movk, typeof(OpCodeMov64)); + SetA64("111100101xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movk, typeof(OpCodeMov64)); + SetA64("0001001010xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movn, typeof(OpCodeMov64)); + SetA64("100100101xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movn, typeof(OpCodeMov64)); + SetA64("0101001010xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movz, typeof(OpCodeMov64)); + SetA64("110100101xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Movz, typeof(OpCodeMov64)); + SetA64("110101010011xxxxxxxxxxxxxxxxxxxx", InstEmit.Mrs, typeof(OpCodeSystem64)); + SetA64("110101010001xxxxxxxxxxxxxxxxxxxx", InstEmit.Msr, typeof(OpCodeSystem64)); + SetA64("x0011011000xxxxx1xxxxxxxxxxxxxxx", InstEmit.Msub, typeof(OpCodeMul64)); + SetA64("11010101000000110010000000011111", InstEmit.Nop, typeof(OpCodeSystem64)); + SetA64("00101010xx1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Orn, typeof(OpCodeAluRs64)); + SetA64("10101010xx1xxxxxxxxxxxxxxxxxxxxx", InstEmit.Orn, typeof(OpCodeAluRs64)); + SetA64("0011001000xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Orr, typeof(OpCodeAluImm64)); + SetA64("101100100xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Orr, typeof(OpCodeAluImm64)); + SetA64("00101010xx0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Orr, typeof(OpCodeAluRs64)); + SetA64("10101010xx0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Orr, typeof(OpCodeAluRs64)); + SetA64("1111100110xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Pfrm, typeof(OpCodeMemImm64)); + SetA64("11111000100xxxxxxxxx00xxxxxxxxxx", InstEmit.Pfrm, typeof(OpCodeMemImm64)); + SetA64("11011000xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Pfrm, typeof(OpCodeMemLit64)); + SetA64("x101101011000000000000xxxxxxxxxx", InstEmit.Rbit, typeof(OpCodeAlu64)); + SetA64("1101011001011111000000xxxxx00000", InstEmit.Ret, typeof(OpCodeBReg64)); + SetA64("x101101011000000000001xxxxxxxxxx", InstEmit.Rev16, typeof(OpCodeAlu64)); + SetA64("x101101011000000000010xxxxxxxxxx", InstEmit.Rev32, typeof(OpCodeAlu64)); + SetA64("1101101011000000000011xxxxxxxxxx", InstEmit.Rev64, typeof(OpCodeAlu64)); + SetA64("x0011010110xxxxx001011xxxxxxxxxx", InstEmit.Rorv, typeof(OpCodeAluRs64)); + SetA64("x1011010000xxxxx000000xxxxxxxxxx", InstEmit.Sbc, typeof(OpCodeAluRs64)); + SetA64("x1111010000xxxxx000000xxxxxxxxxx", InstEmit.Sbcs, typeof(OpCodeAluRs64)); + SetA64("00010011000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Sbfm, typeof(OpCodeBfm64)); + SetA64("1001001101xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Sbfm, typeof(OpCodeBfm64)); + SetA64("x0011010110xxxxx000011xxxxxxxxxx", InstEmit.Sdiv, typeof(OpCodeAluRs64)); + SetA64("10011011001xxxxx0xxxxxxxxxxxxxxx", InstEmit.Smaddl, typeof(OpCodeMul64)); + SetA64("10011011001xxxxx1xxxxxxxxxxxxxxx", InstEmit.Smsubl, typeof(OpCodeMul64)); + SetA64("10011011010xxxxx0xxxxxxxxxxxxxxx", InstEmit.Smulh, typeof(OpCodeMul64)); + SetA64("xx001000100xxxxx1xxxxxxxxxxxxxxx", InstEmit.Stlr, typeof(OpCodeMemEx64)); + SetA64("1x001000001xxxxx1xxxxxxxxxxxxxxx", InstEmit.Stlxp, typeof(OpCodeMemEx64)); + SetA64("xx001000000xxxxx1xxxxxxxxxxxxxxx", InstEmit.Stlxr, typeof(OpCodeMemEx64)); + SetA64("x010100xx0xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Stp, typeof(OpCodeMemPair64)); + SetA64("xx111000000xxxxxxxxxxxxxxxxxxxxx", InstEmit.Str, typeof(OpCodeMemImm64)); + SetA64("xx11100100xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Str, typeof(OpCodeMemImm64)); + SetA64("xx111000001xxxxxxxxx10xxxxxxxxxx", InstEmit.Str, typeof(OpCodeMemReg64)); + SetA64("1x001000001xxxxx0xxxxxxxxxxxxxxx", InstEmit.Stxp, typeof(OpCodeMemEx64)); + SetA64("xx001000000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Stxr, typeof(OpCodeMemEx64)); + SetA64("x10100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluImm64)); + SetA64("01001011<<0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluRs64)); + SetA64("11001011<<0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluRs64)); + SetA64("x1001011001xxxxxxxx0xxxxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluRx64)); + SetA64("x1001011001xxxxxxxx100xxxxxxxxxx", InstEmit.Sub, typeof(OpCodeAluRx64)); + SetA64("x11100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluImm64)); + SetA64("01101011<<0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluRs64)); + SetA64("11101011<<0xxxxxxxxxxxxxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluRs64)); + SetA64("x1101011001xxxxxxxx0xxxxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluRx64)); + SetA64("x1101011001xxxxxxxx100xxxxxxxxxx", InstEmit.Subs, typeof(OpCodeAluRx64)); + SetA64("11010100000xxxxxxxxxxxxxxxx00001", InstEmit.Svc, typeof(OpCodeException64)); + SetA64("1101010100001xxxxxxxxxxxxxxxxxxx", InstEmit.Sys, typeof(OpCodeSystem64)); + SetA64("x0110111xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Tbnz, typeof(OpCodeBImmTest64)); + SetA64("x0110110xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Tbz, typeof(OpCodeBImmTest64)); + SetA64("01010011000xxxxx0xxxxxxxxxxxxxxx", InstEmit.Ubfm, typeof(OpCodeBfm64)); + SetA64("1101001101xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ubfm, typeof(OpCodeBfm64)); + SetA64("x0011010110xxxxx000010xxxxxxxxxx", InstEmit.Udiv, typeof(OpCodeAluRs64)); + SetA64("10011011101xxxxx0xxxxxxxxxxxxxxx", InstEmit.Umaddl, typeof(OpCodeMul64)); + SetA64("10011011101xxxxx1xxxxxxxxxxxxxxx", InstEmit.Umsubl, typeof(OpCodeMul64)); + SetA64("10011011110xxxxx0xxxxxxxxxxxxxxx", InstEmit.Umulh, typeof(OpCodeMul64)); + + //Vector + SetA64("0101111011100000101110xxxxxxxxxx", InstEmit.Abs_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<100000101110xxxxxxxxxx", InstEmit.Abs_V, typeof(OpCodeSimd64)); + SetA64("01011110111xxxxx100001xxxxxxxxxx", InstEmit.Add_S, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<1xxxxx100001xxxxxxxxxx", InstEmit.Add_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx010000xxxxxxxxxx", InstEmit.Addhn_V, typeof(OpCodeSimdReg64)); + SetA64("0101111011110001101110xxxxxxxxxx", InstEmit.Addp_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<1xxxxx101111xxxxxxxxxx", InstEmit.Addp_V, typeof(OpCodeSimdReg64)); + SetA64("000011100x110001101110xxxxxxxxxx", InstEmit.Addv_V, typeof(OpCodeSimd64)); + SetA64("01001110<<110001101110xxxxxxxxxx", InstEmit.Addv_V, typeof(OpCodeSimd64)); + SetA64("0100111000101000010110xxxxxxxxxx", InstEmit.Aesd_V, typeof(OpCodeSimd64)); + SetA64("0100111000101000010010xxxxxxxxxx", InstEmit.Aese_V, typeof(OpCodeSimd64)); + SetA64("0100111000101000011110xxxxxxxxxx", InstEmit.Aesimc_V, typeof(OpCodeSimd64)); + SetA64("0100111000101000011010xxxxxxxxxx", InstEmit.Aesmc_V, typeof(OpCodeSimd64)); + SetA64("0x001110001xxxxx000111xxxxxxxxxx", InstEmit.And_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110011xxxxx000111xxxxxxxxxx", InstEmit.Bic_V, typeof(OpCodeSimdReg64)); + SetA64("0x10111100000xxx<101110<<1xxxxx100011xxxxxxxxxx", InstEmit.Cmeq_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<100000100110xxxxxxxxxx", InstEmit.Cmeq_V, typeof(OpCodeSimd64)); + SetA64("01011110111xxxxx001111xxxxxxxxxx", InstEmit.Cmge_S, typeof(OpCodeSimdReg64)); + SetA64("0111111011100000100010xxxxxxxxxx", InstEmit.Cmge_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<1xxxxx001111xxxxxxxxxx", InstEmit.Cmge_V, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<100000100010xxxxxxxxxx", InstEmit.Cmge_V, typeof(OpCodeSimd64)); + SetA64("01011110111xxxxx001101xxxxxxxxxx", InstEmit.Cmgt_S, typeof(OpCodeSimdReg64)); + SetA64("0101111011100000100010xxxxxxxxxx", InstEmit.Cmgt_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<1xxxxx001101xxxxxxxxxx", InstEmit.Cmgt_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<100000100010xxxxxxxxxx", InstEmit.Cmgt_V, typeof(OpCodeSimd64)); + SetA64("01111110111xxxxx001101xxxxxxxxxx", InstEmit.Cmhi_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx001101xxxxxxxxxx", InstEmit.Cmhi_V, typeof(OpCodeSimdReg64)); + SetA64("01111110111xxxxx001111xxxxxxxxxx", InstEmit.Cmhs_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx001111xxxxxxxxxx", InstEmit.Cmhs_V, typeof(OpCodeSimdReg64)); + SetA64("0111111011100000100110xxxxxxxxxx", InstEmit.Cmle_S, typeof(OpCodeSimd64)); + SetA64("0>101110<<100000100110xxxxxxxxxx", InstEmit.Cmle_V, typeof(OpCodeSimd64)); + SetA64("0101111011100000101010xxxxxxxxxx", InstEmit.Cmlt_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<100000101010xxxxxxxxxx", InstEmit.Cmlt_V, typeof(OpCodeSimd64)); + SetA64("01011110111xxxxx100011xxxxxxxxxx", InstEmit.Cmtst_S, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<1xxxxx100011xxxxxxxxxx", InstEmit.Cmtst_V, typeof(OpCodeSimdReg64)); + SetA64("0x00111000100000010110xxxxxxxxxx", InstEmit.Cnt_V, typeof(OpCodeSimd64)); + SetA64("0>001110000x<>>>000011xxxxxxxxxx", InstEmit.Dup_Gp, typeof(OpCodeSimdIns64)); + SetA64("01011110000xxxxx000001xxxxxxxxxx", InstEmit.Dup_S, typeof(OpCodeSimdIns64)); + SetA64("0>001110000x<>>>000001xxxxxxxxxx", InstEmit.Dup_V, typeof(OpCodeSimdIns64)); + SetA64("0x101110001xxxxx000111xxxxxxxxxx", InstEmit.Eor_V, typeof(OpCodeSimdReg64)); + SetA64("0>101110000xxxxx00011101<100000111110xxxxxxxxxx", InstEmit.Fabs_V, typeof(OpCodeSimd64)); + SetA64("000111100x1xxxxx001010xxxxxxxxxx", InstEmit.Fadd_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011100<1xxxxx110101xxxxxxxxxx", InstEmit.Fadd_V, typeof(OpCodeSimdReg64)); + SetA64("011111100x110000110110xxxxxxxxxx", InstEmit.Faddp_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<1xxxxx110101xxxxxxxxxx", InstEmit.Faddp_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxxxxxx01xxxxx0xxxx", InstEmit.Fccmp_S, typeof(OpCodeSimdFcond64)); + SetA64("000111100x1xxxxxxxxx01xxxxx1xxxx", InstEmit.Fccmpe_S, typeof(OpCodeSimdFcond64)); + SetA64("010111100x1xxxxx111001xxxxxxxxxx", InstEmit.Fcmeq_S, typeof(OpCodeSimdReg64)); + SetA64("010111101x100000110110xxxxxxxxxx", InstEmit.Fcmeq_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<1xxxxx111001xxxxxxxxxx", InstEmit.Fcmeq_V, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<100000110110xxxxxxxxxx", InstEmit.Fcmeq_V, typeof(OpCodeSimd64)); + SetA64("011111100x1xxxxx111001xxxxxxxxxx", InstEmit.Fcmge_S, typeof(OpCodeSimdReg64)); + SetA64("011111101x100000110010xxxxxxxxxx", InstEmit.Fcmge_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<1xxxxx111001xxxxxxxxxx", InstEmit.Fcmge_V, typeof(OpCodeSimdReg64)); + SetA64("0>1011101<100000110010xxxxxxxxxx", InstEmit.Fcmge_V, typeof(OpCodeSimd64)); + SetA64("011111101x1xxxxx111001xxxxxxxxxx", InstEmit.Fcmgt_S, typeof(OpCodeSimdReg64)); + SetA64("010111101x100000110010xxxxxxxxxx", InstEmit.Fcmgt_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<1xxxxx111001xxxxxxxxxx", InstEmit.Fcmgt_V, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<100000110010xxxxxxxxxx", InstEmit.Fcmgt_V, typeof(OpCodeSimd64)); + SetA64("011111101x100000110110xxxxxxxxxx", InstEmit.Fcmle_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100000110110xxxxxxxxxx", InstEmit.Fcmle_V, typeof(OpCodeSimd64)); + SetA64("010111101x100000111010xxxxxxxxxx", InstEmit.Fcmlt_S, typeof(OpCodeSimd64)); + SetA64("0>0011101<100000111010xxxxxxxxxx", InstEmit.Fcmlt_V, typeof(OpCodeSimd64)); + SetA64("000111100x1xxxxx001000xxxxx0x000", InstEmit.Fcmp_S, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx001000xxxxx1x000", InstEmit.Fcmpe_S, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxxxxxx11xxxxxxxxxx", InstEmit.Fcsel_S, typeof(OpCodeSimdFcond64)); + SetA64("000111100x10001xx10000xxxxxxxxxx", InstEmit.Fcvt_S, typeof(OpCodeSimd64)); + SetA64("x00111100x100100000000xxxxxxxxxx", InstEmit.Fcvtas_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x100101000000xxxxxxxxxx", InstEmit.Fcvtau_Gp, typeof(OpCodeSimdCvt64)); + SetA64("0x0011100x100001011110xxxxxxxxxx", InstEmit.Fcvtl_V, typeof(OpCodeSimd64)); + SetA64("x00111100x110000000000xxxxxxxxxx", InstEmit.Fcvtms_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x110001000000xxxxxxxxxx", InstEmit.Fcvtmu_Gp, typeof(OpCodeSimdCvt64)); + SetA64("0x0011100x100001011010xxxxxxxxxx", InstEmit.Fcvtn_V, typeof(OpCodeSimd64)); + SetA64("010111100x100001101010xxxxxxxxxx", InstEmit.Fcvtns_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<100001101010xxxxxxxxxx", InstEmit.Fcvtns_V, typeof(OpCodeSimd64)); + SetA64("011111100x100001101010xxxxxxxxxx", InstEmit.Fcvtnu_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<100001101010xxxxxxxxxx", InstEmit.Fcvtnu_V, typeof(OpCodeSimd64)); + SetA64("x00111100x101000000000xxxxxxxxxx", InstEmit.Fcvtps_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x101001000000xxxxxxxxxx", InstEmit.Fcvtpu_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x111000000000xxxxxxxxxx", InstEmit.Fcvtzs_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x011000xxxxxxxxxxxxxxxx", InstEmit.Fcvtzs_Gp_Fix, typeof(OpCodeSimdCvt64)); + SetA64("010111101x100001101110xxxxxxxxxx", InstEmit.Fcvtzs_S, typeof(OpCodeSimd64)); + SetA64("0>0011101<100001101110xxxxxxxxxx", InstEmit.Fcvtzs_V, typeof(OpCodeSimd64)); + SetA64("0x0011110>>xxxxx111111xxxxxxxxxx", InstEmit.Fcvtzs_V, typeof(OpCodeSimdShImm64)); + SetA64("x00111100x111001000000xxxxxxxxxx", InstEmit.Fcvtzu_Gp, typeof(OpCodeSimdCvt64)); + SetA64("x00111100x011001xxxxxxxxxxxxxxxx", InstEmit.Fcvtzu_Gp_Fix, typeof(OpCodeSimdCvt64)); + SetA64("011111101x100001101110xxxxxxxxxx", InstEmit.Fcvtzu_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100001101110xxxxxxxxxx", InstEmit.Fcvtzu_V, typeof(OpCodeSimd64)); + SetA64("0x1011110>>xxxxx111111xxxxxxxxxx", InstEmit.Fcvtzu_V, typeof(OpCodeSimdShImm64)); + SetA64("000111100x1xxxxx000110xxxxxxxxxx", InstEmit.Fdiv_S, typeof(OpCodeSimdReg64)); + SetA64("0>1011100<1xxxxx111111xxxxxxxxxx", InstEmit.Fdiv_V, typeof(OpCodeSimdReg64)); + SetA64("000111110x0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Fmadd_S, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx010010xxxxxxxxxx", InstEmit.Fmax_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011100<1xxxxx111101xxxxxxxxxx", InstEmit.Fmax_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx011010xxxxxxxxxx", InstEmit.Fmaxnm_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011100<1xxxxx110001xxxxxxxxxx", InstEmit.Fmaxnm_V, typeof(OpCodeSimdReg64)); + SetA64("0>1011100<1xxxxx111101xxxxxxxxxx", InstEmit.Fmaxp_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx010110xxxxxxxxxx", InstEmit.Fmin_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<1xxxxx111101xxxxxxxxxx", InstEmit.Fmin_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx011110xxxxxxxxxx", InstEmit.Fminnm_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<1xxxxx110001xxxxxxxxxx", InstEmit.Fminnm_V, typeof(OpCodeSimdReg64)); + SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", InstEmit.Fminp_V, typeof(OpCodeSimdReg64)); + SetA64("010111111xxxxxxx0001x0xxxxxxxxxx", InstEmit.Fmla_Se, typeof(OpCodeSimdRegElemF64)); + SetA64("0>0011100<1xxxxx110011xxxxxxxxxx", InstEmit.Fmla_V, typeof(OpCodeSimdReg64)); + SetA64("0>00111110011101<1xxxxx110011xxxxxxxxxx", InstEmit.Fmls_V, typeof(OpCodeSimdReg64)); + SetA64("0>00111111011100<1xxxxx110111xxxxxxxxxx", InstEmit.Fmul_V, typeof(OpCodeSimdReg64)); + SetA64("0>00111110011100<1xxxxx110111xxxxxxxxxx", InstEmit.Fmulx_V, typeof(OpCodeSimdReg64)); + SetA64("0>10111111011101<100000111110xxxxxxxxxx", InstEmit.Fneg_V, typeof(OpCodeSimd64)); + SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", InstEmit.Fnmadd_S, typeof(OpCodeSimdReg64)); + SetA64("000111110x1xxxxx1xxxxxxxxxxxxxxx", InstEmit.Fnmsub_S, typeof(OpCodeSimdReg64)); + SetA64("000111100x1xxxxx100010xxxxxxxxxx", InstEmit.Fnmul_S, typeof(OpCodeSimdReg64)); + SetA64("010111101x100001110110xxxxxxxxxx", InstEmit.Frecpe_S, typeof(OpCodeSimd64)); + SetA64("0>0011101<100001110110xxxxxxxxxx", InstEmit.Frecpe_V, typeof(OpCodeSimd64)); + SetA64("010111100x1xxxxx111111xxxxxxxxxx", InstEmit.Frecps_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011100<1xxxxx111111xxxxxxxxxx", InstEmit.Frecps_V, typeof(OpCodeSimdReg64)); + SetA64("010111101x100001111110xxxxxxxxxx", InstEmit.Frecpx_S, typeof(OpCodeSimd64)); + SetA64("000111100x100110010000xxxxxxxxxx", InstEmit.Frinta_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<100001100010xxxxxxxxxx", InstEmit.Frinta_V, typeof(OpCodeSimd64)); + SetA64("000111100x100111110000xxxxxxxxxx", InstEmit.Frinti_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100001100110xxxxxxxxxx", InstEmit.Frinti_V, typeof(OpCodeSimd64)); + SetA64("000111100x100101010000xxxxxxxxxx", InstEmit.Frintm_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<100001100110xxxxxxxxxx", InstEmit.Frintm_V, typeof(OpCodeSimd64)); + SetA64("000111100x100100010000xxxxxxxxxx", InstEmit.Frintn_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<100001100010xxxxxxxxxx", InstEmit.Frintn_V, typeof(OpCodeSimd64)); + SetA64("000111100x100100110000xxxxxxxxxx", InstEmit.Frintp_S, typeof(OpCodeSimd64)); + SetA64("0>0011101<100001100010xxxxxxxxxx", InstEmit.Frintp_V, typeof(OpCodeSimd64)); + SetA64("000111100x100111010000xxxxxxxxxx", InstEmit.Frintx_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<100001100110xxxxxxxxxx", InstEmit.Frintx_V, typeof(OpCodeSimd64)); + SetA64("011111101x100001110110xxxxxxxxxx", InstEmit.Frsqrte_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100001110110xxxxxxxxxx", InstEmit.Frsqrte_V, typeof(OpCodeSimd64)); + SetA64("010111101x1xxxxx111111xxxxxxxxxx", InstEmit.Frsqrts_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<1xxxxx111111xxxxxxxxxx", InstEmit.Frsqrts_V, typeof(OpCodeSimdReg64)); + SetA64("000111100x100001110000xxxxxxxxxx", InstEmit.Fsqrt_S, typeof(OpCodeSimd64)); + SetA64("0>1011101<100001111110xxxxxxxxxx", InstEmit.Fsqrt_V, typeof(OpCodeSimd64)); + SetA64("000111100x1xxxxx001110xxxxxxxxxx", InstEmit.Fsub_S, typeof(OpCodeSimdReg64)); + SetA64("0>0011101<1xxxxx110101xxxxxxxxxx", InstEmit.Fsub_V, typeof(OpCodeSimdReg64)); + SetA64("01001110000xxxxx000111xxxxxxxxxx", InstEmit.Ins_Gp, typeof(OpCodeSimdIns64)); + SetA64("01101110000xxxxx0xxxx1xxxxxxxxxx", InstEmit.Ins_V, typeof(OpCodeSimdIns64)); + SetA64("0x00110001000000xxxxxxxxxxxxxxxx", InstEmit.Ld__Vms, typeof(OpCodeSimdMemMs64)); + SetA64("0x001100110xxxxxxxxxxxxxxxxxxxxx", InstEmit.Ld__Vms, typeof(OpCodeSimdMemMs64)); + SetA64("0x00110101x00000xxxxxxxxxxxxxxxx", InstEmit.Ld__Vss, typeof(OpCodeSimdMemSs64)); + SetA64("0x00110111xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ld__Vss, typeof(OpCodeSimdMemSs64)); + SetA64("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldp, typeof(OpCodeSimdMemPair64)); + SetA64("xx111100x10xxxxxxxxx00xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x10xxxxxxxxx01xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x10xxxxxxxxx11xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemImm64)); + SetA64("xx111101x1xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x11xxxxxxxxx10xxxxxxxxxx", InstEmit.Ldr, typeof(OpCodeSimdMemReg64)); + SetA64("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit.LdrLit, typeof(OpCodeSimdMemLit64)); + SetA64("0x001110<<1xxxxx100101xxxxxxxxxx", InstEmit.Mla_V, typeof(OpCodeSimdReg64)); + SetA64("0x101111xxxxxxxx0000x0xxxxxxxxxx", InstEmit.Mla_Ve, typeof(OpCodeSimdRegElem64)); + SetA64("0x101110<<1xxxxx100101xxxxxxxxxx", InstEmit.Mls_V, typeof(OpCodeSimdReg64)); + SetA64("0x101111xxxxxxxx0100x0xxxxxxxxxx", InstEmit.Mls_Ve, typeof(OpCodeSimdRegElem64)); + SetA64("0x00111100000xxx0xx001xxxxxxxxxx", InstEmit.Movi_V, typeof(OpCodeSimdImm64)); + SetA64("0x00111100000xxx10x001xxxxxxxxxx", InstEmit.Movi_V, typeof(OpCodeSimdImm64)); + SetA64("0x00111100000xxx110x01xxxxxxxxxx", InstEmit.Movi_V, typeof(OpCodeSimdImm64)); + SetA64("0xx0111100000xxx111001xxxxxxxxxx", InstEmit.Movi_V, typeof(OpCodeSimdImm64)); + SetA64("0x001110<<1xxxxx100111xxxxxxxxxx", InstEmit.Mul_V, typeof(OpCodeSimdReg64)); + SetA64("0x001111xxxxxxxx1000x0xxxxxxxxxx", InstEmit.Mul_Ve, typeof(OpCodeSimdRegElem64)); + SetA64("0x10111100000xxx0xx001xxxxxxxxxx", InstEmit.Mvni_V, typeof(OpCodeSimdImm64)); + SetA64("0x10111100000xxx10x001xxxxxxxxxx", InstEmit.Mvni_V, typeof(OpCodeSimdImm64)); + SetA64("0x10111100000xxx110x01xxxxxxxxxx", InstEmit.Mvni_V, typeof(OpCodeSimdImm64)); + SetA64("0111111011100000101110xxxxxxxxxx", InstEmit.Neg_S, typeof(OpCodeSimd64)); + SetA64("0>101110<<100000101110xxxxxxxxxx", InstEmit.Neg_V, typeof(OpCodeSimd64)); + SetA64("0x10111000100000010110xxxxxxxxxx", InstEmit.Not_V, typeof(OpCodeSimd64)); + SetA64("0x001110111xxxxx000111xxxxxxxxxx", InstEmit.Orn_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110101xxxxx000111xxxxxxxxxx", InstEmit.Orr_V, typeof(OpCodeSimdReg64)); + SetA64("0x00111100000xxx<>>xxx100011xxxxxxxxxx", InstEmit.Rshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0x101110<<1xxxxx011000xxxxxxxxxx", InstEmit.Rsubhn_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011111xxxxxxxxxx", InstEmit.Saba_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx010100xxxxxxxxxx", InstEmit.Sabal_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011101xxxxxxxxxx", InstEmit.Sabd_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011100xxxxxxxxxx", InstEmit.Sabdl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<100000011010xxxxxxxxxx", InstEmit.Sadalp_V, typeof(OpCodeSimd64)); + SetA64("0x001110<<1xxxxx000000xxxxxxxxxx", InstEmit.Saddl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<100000001010xxxxxxxxxx", InstEmit.Saddlp_V, typeof(OpCodeSimd64)); + SetA64("0x001110<<1xxxxx000100xxxxxxxxxx", InstEmit.Saddw_V, typeof(OpCodeSimdReg64)); + SetA64("x00111100x100010000000xxxxxxxxxx", InstEmit.Scvtf_Gp, typeof(OpCodeSimdCvt64)); + SetA64("010111100x100001110110xxxxxxxxxx", InstEmit.Scvtf_S, typeof(OpCodeSimd64)); + SetA64("0>0011100<100001110110xxxxxxxxxx", InstEmit.Scvtf_V, typeof(OpCodeSimd64)); + SetA64("01011110000xxxxx000000xxxxxxxxxx", InstEmit.Sha1c_V, typeof(OpCodeSimdReg64)); + SetA64("0101111000101000000010xxxxxxxxxx", InstEmit.Sha1h_V, typeof(OpCodeSimd64)); + SetA64("01011110000xxxxx001000xxxxxxxxxx", InstEmit.Sha1m_V, typeof(OpCodeSimdReg64)); + SetA64("01011110000xxxxx000100xxxxxxxxxx", InstEmit.Sha1p_V, typeof(OpCodeSimdReg64)); + SetA64("01011110000xxxxx001100xxxxxxxxxx", InstEmit.Sha1su0_V, typeof(OpCodeSimdReg64)); + SetA64("0101111000101000000110xxxxxxxxxx", InstEmit.Sha1su1_V, typeof(OpCodeSimd64)); + SetA64("01011110000xxxxx010000xxxxxxxxxx", InstEmit.Sha256h_V, typeof(OpCodeSimdReg64)); + SetA64("01011110000xxxxx010100xxxxxxxxxx", InstEmit.Sha256h2_V, typeof(OpCodeSimdReg64)); + SetA64("0101111000101000001010xxxxxxxxxx", InstEmit.Sha256su0_V, typeof(OpCodeSimd64)); + SetA64("01011110000xxxxx011000xxxxxxxxxx", InstEmit.Sha256su1_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx000001xxxxxxxxxx", InstEmit.Shadd_V, typeof(OpCodeSimdReg64)); + SetA64("0101111101xxxxxx010101xxxxxxxxxx", InstEmit.Shl_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx010101xxxxxxxxxx", InstEmit.Shl_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx010101xxxxxxxxxx", InstEmit.Shl_V, typeof(OpCodeSimdShImm64)); + SetA64("0x101110<<100001001110xxxxxxxxxx", InstEmit.Shll_V, typeof(OpCodeSimd64)); + SetA64("0x00111100>>>xxx100001xxxxxxxxxx", InstEmit.Shrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0x001110<<1xxxxx001001xxxxxxxxxx", InstEmit.Shsub_V, typeof(OpCodeSimdReg64)); + SetA64("0x1011110>>>>xxx010101xxxxxxxxxx", InstEmit.Sli_V, typeof(OpCodeSimdShImm64)); + SetA64("0x001110<<1xxxxx011001xxxxxxxxxx", InstEmit.Smax_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx101001xxxxxxxxxx", InstEmit.Smaxp_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", InstEmit.Smin_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", InstEmit.Sminp_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", InstEmit.Smlal_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", InstEmit.Smlsl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110000xxxxx001011xxxxxxxxxx", InstEmit.Smov_S, typeof(OpCodeSimdIns64)); + SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", InstEmit.Smull_V, typeof(OpCodeSimdReg64)); + SetA64("01011110xx100000011110xxxxxxxxxx", InstEmit.Sqabs_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<100000011110xxxxxxxxxx", InstEmit.Sqabs_V, typeof(OpCodeSimd64)); + SetA64("01011110xx1xxxxx000011xxxxxxxxxx", InstEmit.Sqadd_S, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", InstEmit.Sqadd_V, typeof(OpCodeSimdReg64)); + SetA64("01011110011xxxxx101101xxxxxxxxxx", InstEmit.Sqdmulh_S, typeof(OpCodeSimdReg64)); + SetA64("01011110101xxxxx101101xxxxxxxxxx", InstEmit.Sqdmulh_S, typeof(OpCodeSimdReg64)); + SetA64("0x001110011xxxxx101101xxxxxxxxxx", InstEmit.Sqdmulh_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110101xxxxx101101xxxxxxxxxx", InstEmit.Sqdmulh_V, typeof(OpCodeSimdReg64)); + SetA64("01111110xx100000011110xxxxxxxxxx", InstEmit.Sqneg_S, typeof(OpCodeSimd64)); + SetA64("0>101110<<100000011110xxxxxxxxxx", InstEmit.Sqneg_V, typeof(OpCodeSimd64)); + SetA64("01111110011xxxxx101101xxxxxxxxxx", InstEmit.Sqrdmulh_S, typeof(OpCodeSimdReg64)); + SetA64("01111110101xxxxx101101xxxxxxxxxx", InstEmit.Sqrdmulh_S, typeof(OpCodeSimdReg64)); + SetA64("0x101110011xxxxx101101xxxxxxxxxx", InstEmit.Sqrdmulh_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110101xxxxx101101xxxxxxxxxx", InstEmit.Sqrdmulh_V, typeof(OpCodeSimdReg64)); + SetA64("0101111100>>>xxx100111xxxxxxxxxx", InstEmit.Sqrshrn_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx100111xxxxxxxxxx", InstEmit.Sqrshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111100>>>xxx100011xxxxxxxxxx", InstEmit.Sqrshrun_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx100011xxxxxxxxxx", InstEmit.Sqrshrun_V, typeof(OpCodeSimdShImm64)); + SetA64("0101111100>>>xxx100101xxxxxxxxxx", InstEmit.Sqshrn_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx100101xxxxxxxxxx", InstEmit.Sqshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111100>>>xxx100001xxxxxxxxxx", InstEmit.Sqshrun_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx100001xxxxxxxxxx", InstEmit.Sqshrun_V, typeof(OpCodeSimdShImm64)); + SetA64("01011110xx1xxxxx001011xxxxxxxxxx", InstEmit.Sqsub_S, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<1xxxxx001011xxxxxxxxxx", InstEmit.Sqsub_V, typeof(OpCodeSimdReg64)); + SetA64("01011110<<100001010010xxxxxxxxxx", InstEmit.Sqxtn_S, typeof(OpCodeSimd64)); + SetA64("0x001110<<100001010010xxxxxxxxxx", InstEmit.Sqxtn_V, typeof(OpCodeSimd64)); + SetA64("01111110<<100001001010xxxxxxxxxx", InstEmit.Sqxtun_S, typeof(OpCodeSimd64)); + SetA64("0x101110<<100001001010xxxxxxxxxx", InstEmit.Sqxtun_V, typeof(OpCodeSimd64)); + SetA64("0x001110<<1xxxxx000101xxxxxxxxxx", InstEmit.Srhadd_V, typeof(OpCodeSimdReg64)); + SetA64("0101111101xxxxxx001001xxxxxxxxxx", InstEmit.Srshr_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx001001xxxxxxxxxx", InstEmit.Srshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx001001xxxxxxxxxx", InstEmit.Srshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0101111101xxxxxx001101xxxxxxxxxx", InstEmit.Srsra_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx001101xxxxxxxxxx", InstEmit.Srsra_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx001101xxxxxxxxxx", InstEmit.Srsra_V, typeof(OpCodeSimdShImm64)); + SetA64("0>001110<<1xxxxx010001xxxxxxxxxx", InstEmit.Sshl_V, typeof(OpCodeSimdReg64)); + SetA64("0x00111100>>>xxx101001xxxxxxxxxx", InstEmit.Sshll_V, typeof(OpCodeSimdShImm64)); + SetA64("0101111101xxxxxx000001xxxxxxxxxx", InstEmit.Sshr_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx000001xxxxxxxxxx", InstEmit.Sshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx000001xxxxxxxxxx", InstEmit.Sshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0101111101xxxxxx000101xxxxxxxxxx", InstEmit.Ssra_S, typeof(OpCodeSimdShImm64)); + SetA64("0x00111100>>>xxx000101xxxxxxxxxx", InstEmit.Ssra_V, typeof(OpCodeSimdShImm64)); + SetA64("0100111101xxxxxx000101xxxxxxxxxx", InstEmit.Ssra_V, typeof(OpCodeSimdShImm64)); + SetA64("0x001110<<1xxxxx001000xxxxxxxxxx", InstEmit.Ssubl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx001100xxxxxxxxxx", InstEmit.Ssubw_V, typeof(OpCodeSimdReg64)); + SetA64("0x00110000000000xxxxxxxxxxxxxxxx", InstEmit.St__Vms, typeof(OpCodeSimdMemMs64)); + SetA64("0x001100100xxxxxxxxxxxxxxxxxxxxx", InstEmit.St__Vms, typeof(OpCodeSimdMemMs64)); + SetA64("0x00110100x00000xxxxxxxxxxxxxxxx", InstEmit.St__Vss, typeof(OpCodeSimdMemSs64)); + SetA64("0x00110110xxxxxxxxxxxxxxxxxxxxxx", InstEmit.St__Vss, typeof(OpCodeSimdMemSs64)); + SetA64("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Stp, typeof(OpCodeSimdMemPair64)); + SetA64("xx111100x00xxxxxxxxx00xxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x00xxxxxxxxx01xxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x00xxxxxxxxx11xxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemImm64)); + SetA64("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemImm64)); + SetA64("xx111100x01xxxxxxxxx10xxxxxxxxxx", InstEmit.Str, typeof(OpCodeSimdMemReg64)); + SetA64("01111110111xxxxx100001xxxxxxxxxx", InstEmit.Sub_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx100001xxxxxxxxxx", InstEmit.Sub_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<1xxxxx011000xxxxxxxxxx", InstEmit.Subhn_V, typeof(OpCodeSimdReg64)); + SetA64("01011110xx100000001110xxxxxxxxxx", InstEmit.Suqadd_S, typeof(OpCodeSimd64)); + SetA64("0>001110<<100000001110xxxxxxxxxx", InstEmit.Suqadd_V, typeof(OpCodeSimd64)); + SetA64("0x001110000xxxxx0xx000xxxxxxxxxx", InstEmit.Tbl_V, typeof(OpCodeSimdTbl64)); + SetA64("0>001110<<0xxxxx001010xxxxxxxxxx", InstEmit.Trn1_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<0xxxxx011010xxxxxxxxxx", InstEmit.Trn2_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011111xxxxxxxxxx", InstEmit.Uaba_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx010100xxxxxxxxxx", InstEmit.Uabal_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011101xxxxxxxxxx", InstEmit.Uabd_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011100xxxxxxxxxx", InstEmit.Uabdl_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<100000011010xxxxxxxxxx", InstEmit.Uadalp_V, typeof(OpCodeSimd64)); + SetA64("0x101110<<1xxxxx000000xxxxxxxxxx", InstEmit.Uaddl_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<100000001010xxxxxxxxxx", InstEmit.Uaddlp_V, typeof(OpCodeSimd64)); + SetA64("001011100x110000001110xxxxxxxxxx", InstEmit.Uaddlv_V, typeof(OpCodeSimd64)); + SetA64("01101110<<110000001110xxxxxxxxxx", InstEmit.Uaddlv_V, typeof(OpCodeSimd64)); + SetA64("0x101110<<1xxxxx000100xxxxxxxxxx", InstEmit.Uaddw_V, typeof(OpCodeSimdReg64)); + SetA64("x00111100x100011000000xxxxxxxxxx", InstEmit.Ucvtf_Gp, typeof(OpCodeSimdCvt64)); + SetA64("011111100x100001110110xxxxxxxxxx", InstEmit.Ucvtf_S, typeof(OpCodeSimd64)); + SetA64("0>1011100<100001110110xxxxxxxxxx", InstEmit.Ucvtf_V, typeof(OpCodeSimd64)); + SetA64("0x101110<<1xxxxx000001xxxxxxxxxx", InstEmit.Uhadd_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx001001xxxxxxxxxx", InstEmit.Uhsub_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011001xxxxxxxxxx", InstEmit.Umax_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx101001xxxxxxxxxx", InstEmit.Umaxp_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", InstEmit.Umin_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", InstEmit.Uminp_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", InstEmit.Umlal_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", InstEmit.Umlsl_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110000xxxxx001111xxxxxxxxxx", InstEmit.Umov_S, typeof(OpCodeSimdIns64)); + SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", InstEmit.Umull_V, typeof(OpCodeSimdReg64)); + SetA64("01111110xx1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_V, typeof(OpCodeSimdReg64)); + SetA64("0111111100>>>xxx100111xxxxxxxxxx", InstEmit.Uqrshrn_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx100111xxxxxxxxxx", InstEmit.Uqrshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111100>>>xxx100101xxxxxxxxxx", InstEmit.Uqshrn_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx100101xxxxxxxxxx", InstEmit.Uqshrn_V, typeof(OpCodeSimdShImm64)); + SetA64("01111110xx1xxxxx001011xxxxxxxxxx", InstEmit.Uqsub_S, typeof(OpCodeSimdReg64)); + SetA64("0>101110<<1xxxxx001011xxxxxxxxxx", InstEmit.Uqsub_V, typeof(OpCodeSimdReg64)); + SetA64("01111110<<100001010010xxxxxxxxxx", InstEmit.Uqxtn_S, typeof(OpCodeSimd64)); + SetA64("0x101110<<100001010010xxxxxxxxxx", InstEmit.Uqxtn_V, typeof(OpCodeSimd64)); + SetA64("0x101110<<1xxxxx000101xxxxxxxxxx", InstEmit.Urhadd_V, typeof(OpCodeSimdReg64)); + SetA64("0111111101xxxxxx001001xxxxxxxxxx", InstEmit.Urshr_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx001001xxxxxxxxxx", InstEmit.Urshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0110111101xxxxxx001001xxxxxxxxxx", InstEmit.Urshr_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111101xxxxxx001101xxxxxxxxxx", InstEmit.Ursra_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx001101xxxxxxxxxx", InstEmit.Ursra_V, typeof(OpCodeSimdShImm64)); + SetA64("0110111101xxxxxx001101xxxxxxxxxx", InstEmit.Ursra_V, typeof(OpCodeSimdShImm64)); + SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", InstEmit.Ushl_V, typeof(OpCodeSimdReg64)); + SetA64("0x10111100>>>xxx101001xxxxxxxxxx", InstEmit.Ushll_V, typeof(OpCodeSimdShImm64)); + SetA64("0111111101xxxxxx000001xxxxxxxxxx", InstEmit.Ushr_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx000001xxxxxxxxxx", InstEmit.Ushr_V, typeof(OpCodeSimdShImm64)); + SetA64("0110111101xxxxxx000001xxxxxxxxxx", InstEmit.Ushr_V, typeof(OpCodeSimdShImm64)); + SetA64("01111110xx100000001110xxxxxxxxxx", InstEmit.Usqadd_S, typeof(OpCodeSimd64)); + SetA64("0>101110<<100000001110xxxxxxxxxx", InstEmit.Usqadd_V, typeof(OpCodeSimd64)); + SetA64("0111111101xxxxxx000101xxxxxxxxxx", InstEmit.Usra_S, typeof(OpCodeSimdShImm64)); + SetA64("0x10111100>>>xxx000101xxxxxxxxxx", InstEmit.Usra_V, typeof(OpCodeSimdShImm64)); + SetA64("0110111101xxxxxx000101xxxxxxxxxx", InstEmit.Usra_V, typeof(OpCodeSimdShImm64)); + SetA64("0x101110<<1xxxxx001000xxxxxxxxxx", InstEmit.Usubl_V, typeof(OpCodeSimdReg64)); + SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", InstEmit.Usubw_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<0xxxxx000110xxxxxxxxxx", InstEmit.Uzp1_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<0xxxxx010110xxxxxxxxxx", InstEmit.Uzp2_V, typeof(OpCodeSimdReg64)); + SetA64("0x001110<<100001001010xxxxxxxxxx", InstEmit.Xtn_V, typeof(OpCodeSimd64)); + SetA64("0>001110<<0xxxxx001110xxxxxxxxxx", InstEmit.Zip1_V, typeof(OpCodeSimdReg64)); + SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstEmit.Zip2_V, typeof(OpCodeSimdReg64)); +#endregion + +#region "Generate InstA64FastLookup Table (AArch64)" + var tmp = new List[_fastLookupSize]; + for (int i = 0; i < _fastLookupSize; i++) + { + tmp[i] = new List(); + } + + foreach (var inst in _allInstA64) + { + int mask = ToFastLookupIndex(inst.Mask); + int value = ToFastLookupIndex(inst.Value); + + for (int i = 0; i < _fastLookupSize; i++) + { + if ((i & mask) == value) + { + tmp[i].Add(inst); + } + } + } + + for (int i = 0; i < _fastLookupSize; i++) + { + _instA64FastLookup[i] = tmp[i].ToArray(); + } +#endregion + } + + private class InstInfo + { + public int Mask; + public int Value; + + public Inst Inst; + + public InstInfo(int mask, int value, Inst inst) + { + Mask = mask; + Value = value; + Inst = inst; + } + } + + private static List _allInstA32 = new List(); + private static List _allInstA64 = new List(); + + private static int _fastLookupSize = 0x1000; + private static InstInfo[][] _instA64FastLookup = new InstInfo[_fastLookupSize][]; + + private static void SetA32(string encoding, InstInterpreter interpreter, Type type) + { + Set(encoding, new Inst(interpreter, null, type), ExecutionMode.AArch32); + } + + private static void SetA64(string encoding, InstEmitter emitter, Type type) + { + Set(encoding, new Inst(null, emitter, type), ExecutionMode.AArch64); + } + + private static void Set(string encoding, Inst inst, ExecutionMode mode) + { + int bit = encoding.Length - 1; + int value = 0; + int xMask = 0; + int xBits = 0; + + int[] xPos = new int[encoding.Length]; + + int blacklisted = 0; + + for (int index = 0; index < encoding.Length; index++, bit--) + { + //Note: < and > are used on special encodings. + //The < means that we should never have ALL bits with the '<' set. + //So, when the encoding has <<, it means that 00, 01, and 10 are valid, + //but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on... + //For >, the invalid value is zero. So, for >> 01, 10 and 11 are valid, + //but 00 isn't. + char chr = encoding[index]; + + if (chr == '1') + { + value |= 1 << bit; + } + else if (chr == 'x') + { + xMask |= 1 << bit; + } + else if (chr == '>') + { + xPos[xBits++] = bit; + } + else if (chr == '<') + { + xPos[xBits++] = bit; + + blacklisted |= 1 << bit; + } + else if (chr != '0') + { + throw new ArgumentException(nameof(encoding)); + } + } + + xMask = ~xMask; + + if (xBits == 0) + { + InsertInst(xMask, value, inst, mode); + + return; + } + + for (int index = 0; index < (1 << xBits); index++) + { + int mask = 0; + + for (int x = 0; x < xBits; x++) + { + mask |= ((index >> x) & 1) << xPos[x]; + } + + if (mask != blacklisted) + { + InsertInst(xMask, value | mask, inst, mode); + } + } + } + + private static void InsertInst( + int xMask, + int value, + Inst inst, + ExecutionMode mode) + { + InstInfo info = new InstInfo(xMask, value, inst); + + if (mode == ExecutionMode.AArch64) + { + _allInstA64.Add(info); + } + else + { + _allInstA32.Add(info); + } + } + + public static Inst GetInstA32(int opCode) + { + return GetInstFromList(_allInstA32, opCode); + } + + public static Inst GetInstA64(int opCode) + { + return GetInstFromList(_instA64FastLookup[ToFastLookupIndex(opCode)], opCode); + } + + private static int ToFastLookupIndex(int value) + { + return ((value >> 10) & 0x00F) | ((value >> 18) & 0xFF0); + } + + private static Inst GetInstFromList(IEnumerable instList, int opCode) + { + foreach (var node in instList) + { + if ((opCode & node.Mask) == node.Value) + { + return node.Inst; + } + } + + return Inst.Undefined; + } + } +} diff --git a/ChocolArm64/Optimizations.cs b/ChocolArm64/Optimizations.cs new file mode 100644 index 0000000000..f2b0ffba1a --- /dev/null +++ b/ChocolArm64/Optimizations.cs @@ -0,0 +1,18 @@ +using System.Runtime.Intrinsics.X86; + +public static class Optimizations +{ + internal static bool FastFP = true; + + private static bool _useAllSseIfAvailable = true; + + private static bool _useSseIfAvailable = true; + private static bool _useSse2IfAvailable = true; + private static bool _useSse41IfAvailable = true; + private static bool _useSse42IfAvailable = true; + + internal static bool UseSse = (_useAllSseIfAvailable && _useSseIfAvailable) && Sse.IsSupported; + internal static bool UseSse2 = (_useAllSseIfAvailable && _useSse2IfAvailable) && Sse2.IsSupported; + internal static bool UseSse41 = (_useAllSseIfAvailable && _useSse41IfAvailable) && Sse41.IsSupported; + internal static bool UseSse42 = (_useAllSseIfAvailable && _useSse42IfAvailable) && Sse42.IsSupported; +} diff --git a/ChocolArm64/State/ARegister.cs b/ChocolArm64/State/ARegister.cs deleted file mode 100644 index 5861db8c64..0000000000 --- a/ChocolArm64/State/ARegister.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Reflection; - -namespace ChocolArm64.State -{ - struct ARegister - { - public int Index; - - public ARegisterType Type; - - public ARegister(int Index, ARegisterType Type) - { - this.Index = Index; - this.Type = Type; - } - - public override int GetHashCode() - { - return (ushort)Index | ((ushort)Type << 16); - } - - public override bool Equals(object Obj) - { - return Obj is ARegister Reg && - Reg.Index == Index && - Reg.Type == Type; - } - - public FieldInfo GetField() - { - switch (Type) - { - case ARegisterType.Flag: return GetFieldFlag(); - case ARegisterType.Int: return GetFieldInt(); - case ARegisterType.Vector: return GetFieldVector(); - } - - throw new InvalidOperationException(); - } - - private FieldInfo GetFieldFlag() - { - switch ((APState)Index) - { - case APState.VBit: return GetField(nameof(AThreadState.Overflow)); - case APState.CBit: return GetField(nameof(AThreadState.Carry)); - case APState.ZBit: return GetField(nameof(AThreadState.Zero)); - case APState.NBit: return GetField(nameof(AThreadState.Negative)); - } - - throw new InvalidOperationException(); - } - - private FieldInfo GetFieldInt() - { - switch (Index) - { - case 0: return GetField(nameof(AThreadState.X0)); - case 1: return GetField(nameof(AThreadState.X1)); - case 2: return GetField(nameof(AThreadState.X2)); - case 3: return GetField(nameof(AThreadState.X3)); - case 4: return GetField(nameof(AThreadState.X4)); - case 5: return GetField(nameof(AThreadState.X5)); - case 6: return GetField(nameof(AThreadState.X6)); - case 7: return GetField(nameof(AThreadState.X7)); - case 8: return GetField(nameof(AThreadState.X8)); - case 9: return GetField(nameof(AThreadState.X9)); - case 10: return GetField(nameof(AThreadState.X10)); - case 11: return GetField(nameof(AThreadState.X11)); - case 12: return GetField(nameof(AThreadState.X12)); - case 13: return GetField(nameof(AThreadState.X13)); - case 14: return GetField(nameof(AThreadState.X14)); - case 15: return GetField(nameof(AThreadState.X15)); - case 16: return GetField(nameof(AThreadState.X16)); - case 17: return GetField(nameof(AThreadState.X17)); - case 18: return GetField(nameof(AThreadState.X18)); - case 19: return GetField(nameof(AThreadState.X19)); - case 20: return GetField(nameof(AThreadState.X20)); - case 21: return GetField(nameof(AThreadState.X21)); - case 22: return GetField(nameof(AThreadState.X22)); - case 23: return GetField(nameof(AThreadState.X23)); - case 24: return GetField(nameof(AThreadState.X24)); - case 25: return GetField(nameof(AThreadState.X25)); - case 26: return GetField(nameof(AThreadState.X26)); - case 27: return GetField(nameof(AThreadState.X27)); - case 28: return GetField(nameof(AThreadState.X28)); - case 29: return GetField(nameof(AThreadState.X29)); - case 30: return GetField(nameof(AThreadState.X30)); - case 31: return GetField(nameof(AThreadState.X31)); - } - - throw new InvalidOperationException(); - } - - private FieldInfo GetFieldVector() - { - switch (Index) - { - case 0: return GetField(nameof(AThreadState.V0)); - case 1: return GetField(nameof(AThreadState.V1)); - case 2: return GetField(nameof(AThreadState.V2)); - case 3: return GetField(nameof(AThreadState.V3)); - case 4: return GetField(nameof(AThreadState.V4)); - case 5: return GetField(nameof(AThreadState.V5)); - case 6: return GetField(nameof(AThreadState.V6)); - case 7: return GetField(nameof(AThreadState.V7)); - case 8: return GetField(nameof(AThreadState.V8)); - case 9: return GetField(nameof(AThreadState.V9)); - case 10: return GetField(nameof(AThreadState.V10)); - case 11: return GetField(nameof(AThreadState.V11)); - case 12: return GetField(nameof(AThreadState.V12)); - case 13: return GetField(nameof(AThreadState.V13)); - case 14: return GetField(nameof(AThreadState.V14)); - case 15: return GetField(nameof(AThreadState.V15)); - case 16: return GetField(nameof(AThreadState.V16)); - case 17: return GetField(nameof(AThreadState.V17)); - case 18: return GetField(nameof(AThreadState.V18)); - case 19: return GetField(nameof(AThreadState.V19)); - case 20: return GetField(nameof(AThreadState.V20)); - case 21: return GetField(nameof(AThreadState.V21)); - case 22: return GetField(nameof(AThreadState.V22)); - case 23: return GetField(nameof(AThreadState.V23)); - case 24: return GetField(nameof(AThreadState.V24)); - case 25: return GetField(nameof(AThreadState.V25)); - case 26: return GetField(nameof(AThreadState.V26)); - case 27: return GetField(nameof(AThreadState.V27)); - case 28: return GetField(nameof(AThreadState.V28)); - case 29: return GetField(nameof(AThreadState.V29)); - case 30: return GetField(nameof(AThreadState.V30)); - case 31: return GetField(nameof(AThreadState.V31)); - } - - throw new InvalidOperationException(); - } - - private FieldInfo GetField(string Name) - { - return typeof(AThreadState).GetField(Name); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs deleted file mode 100644 index 7b69d81714..0000000000 --- a/ChocolArm64/State/AThreadState.cs +++ /dev/null @@ -1,142 +0,0 @@ -using ChocolArm64.Events; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.Intrinsics; - -namespace ChocolArm64.State -{ - public class AThreadState - { - internal const int LRIndex = 30; - internal const int ZRIndex = 31; - - internal const int ErgSizeLog2 = 4; - internal const int DczSizeLog2 = 4; - - internal AExecutionMode ExecutionMode; - - //AArch32 state. - public uint R0, R1, R2, R3, - R4, R5, R6, R7, - R8, R9, R10, R11, - R12, R13, R14, R15; - - public bool Thumb; - - //AArch64 state. - public ulong X0, X1, X2, X3, X4, X5, X6, X7, - X8, X9, X10, X11, X12, X13, X14, X15, - X16, X17, X18, X19, X20, X21, X22, X23, - X24, X25, X26, X27, X28, X29, X30, X31; - - public Vector128 V0, V1, V2, V3, V4, V5, V6, V7, - V8, V9, V10, V11, V12, V13, V14, V15, - V16, V17, V18, V19, V20, V21, V22, V23, - V24, V25, V26, V27, V28, V29, V30, V31; - - public bool Overflow; - public bool Carry; - public bool Zero; - public bool Negative; - - public bool Running { get; set; } - - public long TpidrEl0 { get; set; } - public long Tpidr { get; set; } - - public int Fpcr { get; set; } - public int Fpsr { get; set; } - - public int Psr - { - get - { - return (Negative ? (int)APState.N : 0) | - (Zero ? (int)APState.Z : 0) | - (Carry ? (int)APState.C : 0) | - (Overflow ? (int)APState.V : 0); - } - } - - public uint CtrEl0 => 0x8444c004; - public uint DczidEl0 => 0x00000004; - - public ulong CntfrqEl0 { get; set; } - public ulong CntpctEl0 - { - get - { - double Ticks = TickCounter.ElapsedTicks * HostTickFreq; - - return (ulong)(Ticks * CntfrqEl0); - } - } - - public event EventHandler Break; - public event EventHandler SvcCall; - public event EventHandler Undefined; - - private Stack CallStack; - - private static Stopwatch TickCounter; - - private static double HostTickFreq; - - public AThreadState() - { - CallStack = new Stack(); - } - - static AThreadState() - { - HostTickFreq = 1.0 / Stopwatch.Frequency; - - TickCounter = new Stopwatch(); - - TickCounter.Start(); - } - - internal bool Synchronize() - { - return Running; - } - - internal void OnBreak(long Position, int Imm) - { - Break?.Invoke(this, new AInstExceptionEventArgs(Position, Imm)); - } - - internal void OnSvcCall(long Position, int Imm) - { - SvcCall?.Invoke(this, new AInstExceptionEventArgs(Position, Imm)); - } - - internal void OnUndefined(long Position, int RawOpCode) - { - Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode)); - } - - internal void EnterMethod(long Position) - { - CallStack.Push(Position); - } - - internal void ExitMethod() - { - CallStack.TryPop(out _); - } - - internal void JumpMethod(long Position) - { - CallStack.TryPop(out _); - - CallStack.Push(Position); - } - - public long[] GetCallStack() - { - return CallStack.ToArray(); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/State/CpuThreadState.cs b/ChocolArm64/State/CpuThreadState.cs new file mode 100644 index 0000000000..a4ee5d0737 --- /dev/null +++ b/ChocolArm64/State/CpuThreadState.cs @@ -0,0 +1,164 @@ +using ChocolArm64.Events; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace ChocolArm64.State +{ + public class CpuThreadState + { + internal const int LrIndex = 30; + internal const int ZrIndex = 31; + + internal const int ErgSizeLog2 = 4; + internal const int DczSizeLog2 = 4; + + private const int MinInstForCheck = 4000000; + + internal ExecutionMode ExecutionMode; + + //AArch32 state. + public uint R0, R1, R2, R3, + R4, R5, R6, R7, + R8, R9, R10, R11, + R12, R13, R14, R15; + + public bool Thumb; + + //AArch64 state. + public ulong X0, X1, X2, X3, X4, X5, X6, X7, + X8, X9, X10, X11, X12, X13, X14, X15, + X16, X17, X18, X19, X20, X21, X22, X23, + X24, X25, X26, X27, X28, X29, X30, X31; + + public Vector128 V0, V1, V2, V3, V4, V5, V6, V7, + V8, V9, V10, V11, V12, V13, V14, V15, + V16, V17, V18, V19, V20, V21, V22, V23, + V24, V25, V26, V27, V28, V29, V30, V31; + + public bool Overflow; + public bool Carry; + public bool Zero; + public bool Negative; + + public bool Running { get; set; } + public int Core { get; set; } + + private bool _interrupted; + + private int _syncCount; + + public long TpidrEl0 { get; set; } + public long Tpidr { get; set; } + + public int Fpcr { get; set; } + public int Fpsr { get; set; } + + public int Psr + { + get + { + return (Negative ? (int)PState.N : 0) | + (Zero ? (int)PState.Z : 0) | + (Carry ? (int)PState.C : 0) | + (Overflow ? (int)PState.V : 0); + } + } + + public uint CtrEl0 => 0x8444c004; + public uint DczidEl0 => 0x00000004; + + public ulong CntfrqEl0 { get; set; } + public ulong CntpctEl0 + { + get + { + double ticks = _tickCounter.ElapsedTicks * _hostTickFreq; + + return (ulong)(ticks * CntfrqEl0); + } + } + + public event EventHandler Interrupt; + public event EventHandler Break; + public event EventHandler SvcCall; + public event EventHandler Undefined; + + private static Stopwatch _tickCounter; + + private static double _hostTickFreq; + + static CpuThreadState() + { + _hostTickFreq = 1.0 / Stopwatch.Frequency; + + _tickCounter = new Stopwatch(); + + _tickCounter.Start(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool Synchronize(int bbWeight) + { + //Firing a interrupt frequently is expensive, so we only + //do it after a given number of instructions has executed. + _syncCount += bbWeight; + + if (_syncCount >= MinInstForCheck) + { + CheckInterrupt(); + } + + return Running; + } + + internal void RequestInterrupt() + { + _interrupted = true; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void CheckInterrupt() + { + _syncCount = 0; + + if (_interrupted) + { + _interrupted = false; + + Interrupt?.Invoke(this, EventArgs.Empty); + } + } + + internal void OnBreak(long position, int imm) + { + Break?.Invoke(this, new InstExceptionEventArgs(position, imm)); + } + + internal void OnSvcCall(long position, int imm) + { + SvcCall?.Invoke(this, new InstExceptionEventArgs(position, imm)); + } + + internal void OnUndefined(long position, int rawOpCode) + { + Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode)); + } + + internal bool GetFpcrFlag(Fpcr flag) + { + return (Fpcr & (1 << (int)flag)) != 0; + } + + internal void SetFpsrFlag(Fpsr flag) + { + Fpsr |= 1 << (int)flag; + } + + internal RoundMode FPRoundingMode() + { + return (RoundMode)((Fpcr >> (int)State.Fpcr.RMode) & 3); + } + } +} diff --git a/ChocolArm64/State/AExecutionMode.cs b/ChocolArm64/State/ExecutionMode.cs similarity index 76% rename from ChocolArm64/State/AExecutionMode.cs rename to ChocolArm64/State/ExecutionMode.cs index 8632da7747..4b8c17cec0 100644 --- a/ChocolArm64/State/AExecutionMode.cs +++ b/ChocolArm64/State/ExecutionMode.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.State { - enum AExecutionMode + enum ExecutionMode { AArch32, AArch64 diff --git a/ChocolArm64/State/FpExc.cs b/ChocolArm64/State/FpExc.cs new file mode 100644 index 0000000000..5cb7a402ff --- /dev/null +++ b/ChocolArm64/State/FpExc.cs @@ -0,0 +1,12 @@ +namespace ChocolArm64.State +{ + enum FpExc + { + InvalidOp = 0, + DivideByZero = 1, + Overflow = 2, + Underflow = 3, + Inexact = 4, + InputDenorm = 7 + } +} diff --git a/ChocolArm64/State/FpType.cs b/ChocolArm64/State/FpType.cs new file mode 100644 index 0000000000..fc2791063a --- /dev/null +++ b/ChocolArm64/State/FpType.cs @@ -0,0 +1,11 @@ +namespace ChocolArm64.State +{ + enum FpType + { + Nonzero, + Zero, + Infinity, + QNaN, + SNaN + } +} diff --git a/ChocolArm64/State/Fpcr.cs b/ChocolArm64/State/Fpcr.cs new file mode 100644 index 0000000000..908faee5f4 --- /dev/null +++ b/ChocolArm64/State/Fpcr.cs @@ -0,0 +1,11 @@ +namespace ChocolArm64.State +{ + enum Fpcr + { + Ufe = 11, + RMode = 22, + Fz = 24, + Dn = 25, + Ahp = 26 + } +} diff --git a/ChocolArm64/State/Fpsr.cs b/ChocolArm64/State/Fpsr.cs new file mode 100644 index 0000000000..ba551eefaa --- /dev/null +++ b/ChocolArm64/State/Fpsr.cs @@ -0,0 +1,8 @@ +namespace ChocolArm64.State +{ + enum Fpsr + { + Ufc = 3, + Qc = 27 + } +} diff --git a/ChocolArm64/State/APState.cs b/ChocolArm64/State/PState.cs similarity index 73% rename from ChocolArm64/State/APState.cs rename to ChocolArm64/State/PState.cs index f55431a661..40636c8705 100644 --- a/ChocolArm64/State/APState.cs +++ b/ChocolArm64/State/PState.cs @@ -3,7 +3,7 @@ using System; namespace ChocolArm64.State { [Flags] - public enum APState + enum PState { VBit = 28, CBit = 29, @@ -15,9 +15,9 @@ namespace ChocolArm64.State Z = 1 << ZBit, N = 1 << NBit, - NZ = N | Z, - CV = C | V, + Nz = N | Z, + Cv = C | V, - NZCV = NZ | CV + Nzcv = Nz | Cv } -} \ No newline at end of file +} diff --git a/ChocolArm64/State/Register.cs b/ChocolArm64/State/Register.cs new file mode 100644 index 0000000000..ea29e7b6b3 --- /dev/null +++ b/ChocolArm64/State/Register.cs @@ -0,0 +1,142 @@ +using System; +using System.Reflection; + +namespace ChocolArm64.State +{ + struct Register + { + public int Index; + + public RegisterType Type; + + public Register(int index, RegisterType type) + { + Index = index; + Type = type; + } + + public override int GetHashCode() + { + return (ushort)Index | ((ushort)Type << 16); + } + + public override bool Equals(object obj) + { + return obj is Register reg && + reg.Index == Index && + reg.Type == Type; + } + + public FieldInfo GetField() + { + switch (Type) + { + case RegisterType.Flag: return GetFieldFlag(); + case RegisterType.Int: return GetFieldInt(); + case RegisterType.Vector: return GetFieldVector(); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldFlag() + { + switch ((PState)Index) + { + case PState.VBit: return GetField(nameof(CpuThreadState.Overflow)); + case PState.CBit: return GetField(nameof(CpuThreadState.Carry)); + case PState.ZBit: return GetField(nameof(CpuThreadState.Zero)); + case PState.NBit: return GetField(nameof(CpuThreadState.Negative)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldInt() + { + switch (Index) + { + case 0: return GetField(nameof(CpuThreadState.X0)); + case 1: return GetField(nameof(CpuThreadState.X1)); + case 2: return GetField(nameof(CpuThreadState.X2)); + case 3: return GetField(nameof(CpuThreadState.X3)); + case 4: return GetField(nameof(CpuThreadState.X4)); + case 5: return GetField(nameof(CpuThreadState.X5)); + case 6: return GetField(nameof(CpuThreadState.X6)); + case 7: return GetField(nameof(CpuThreadState.X7)); + case 8: return GetField(nameof(CpuThreadState.X8)); + case 9: return GetField(nameof(CpuThreadState.X9)); + case 10: return GetField(nameof(CpuThreadState.X10)); + case 11: return GetField(nameof(CpuThreadState.X11)); + case 12: return GetField(nameof(CpuThreadState.X12)); + case 13: return GetField(nameof(CpuThreadState.X13)); + case 14: return GetField(nameof(CpuThreadState.X14)); + case 15: return GetField(nameof(CpuThreadState.X15)); + case 16: return GetField(nameof(CpuThreadState.X16)); + case 17: return GetField(nameof(CpuThreadState.X17)); + case 18: return GetField(nameof(CpuThreadState.X18)); + case 19: return GetField(nameof(CpuThreadState.X19)); + case 20: return GetField(nameof(CpuThreadState.X20)); + case 21: return GetField(nameof(CpuThreadState.X21)); + case 22: return GetField(nameof(CpuThreadState.X22)); + case 23: return GetField(nameof(CpuThreadState.X23)); + case 24: return GetField(nameof(CpuThreadState.X24)); + case 25: return GetField(nameof(CpuThreadState.X25)); + case 26: return GetField(nameof(CpuThreadState.X26)); + case 27: return GetField(nameof(CpuThreadState.X27)); + case 28: return GetField(nameof(CpuThreadState.X28)); + case 29: return GetField(nameof(CpuThreadState.X29)); + case 30: return GetField(nameof(CpuThreadState.X30)); + case 31: return GetField(nameof(CpuThreadState.X31)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetFieldVector() + { + switch (Index) + { + case 0: return GetField(nameof(CpuThreadState.V0)); + case 1: return GetField(nameof(CpuThreadState.V1)); + case 2: return GetField(nameof(CpuThreadState.V2)); + case 3: return GetField(nameof(CpuThreadState.V3)); + case 4: return GetField(nameof(CpuThreadState.V4)); + case 5: return GetField(nameof(CpuThreadState.V5)); + case 6: return GetField(nameof(CpuThreadState.V6)); + case 7: return GetField(nameof(CpuThreadState.V7)); + case 8: return GetField(nameof(CpuThreadState.V8)); + case 9: return GetField(nameof(CpuThreadState.V9)); + case 10: return GetField(nameof(CpuThreadState.V10)); + case 11: return GetField(nameof(CpuThreadState.V11)); + case 12: return GetField(nameof(CpuThreadState.V12)); + case 13: return GetField(nameof(CpuThreadState.V13)); + case 14: return GetField(nameof(CpuThreadState.V14)); + case 15: return GetField(nameof(CpuThreadState.V15)); + case 16: return GetField(nameof(CpuThreadState.V16)); + case 17: return GetField(nameof(CpuThreadState.V17)); + case 18: return GetField(nameof(CpuThreadState.V18)); + case 19: return GetField(nameof(CpuThreadState.V19)); + case 20: return GetField(nameof(CpuThreadState.V20)); + case 21: return GetField(nameof(CpuThreadState.V21)); + case 22: return GetField(nameof(CpuThreadState.V22)); + case 23: return GetField(nameof(CpuThreadState.V23)); + case 24: return GetField(nameof(CpuThreadState.V24)); + case 25: return GetField(nameof(CpuThreadState.V25)); + case 26: return GetField(nameof(CpuThreadState.V26)); + case 27: return GetField(nameof(CpuThreadState.V27)); + case 28: return GetField(nameof(CpuThreadState.V28)); + case 29: return GetField(nameof(CpuThreadState.V29)); + case 30: return GetField(nameof(CpuThreadState.V30)); + case 31: return GetField(nameof(CpuThreadState.V31)); + } + + throw new InvalidOperationException(); + } + + private FieldInfo GetField(string name) + { + return typeof(CpuThreadState).GetField(name); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/State/ARegisterSize.cs b/ChocolArm64/State/RegisterSize.cs similarity index 57% rename from ChocolArm64/State/ARegisterSize.cs rename to ChocolArm64/State/RegisterSize.cs index 144f36b929..7cc9959931 100644 --- a/ChocolArm64/State/ARegisterSize.cs +++ b/ChocolArm64/State/RegisterSize.cs @@ -1,10 +1,10 @@ namespace ChocolArm64.State { - enum ARegisterSize + enum RegisterSize { Int32, Int64, - SIMD64, - SIMD128 + Simd64, + Simd128 } } \ No newline at end of file diff --git a/ChocolArm64/State/ARegisterType.cs b/ChocolArm64/State/RegisterType.cs similarity index 78% rename from ChocolArm64/State/ARegisterType.cs rename to ChocolArm64/State/RegisterType.cs index f9776bb7dd..4476d04426 100644 --- a/ChocolArm64/State/ARegisterType.cs +++ b/ChocolArm64/State/RegisterType.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.State { - enum ARegisterType + enum RegisterType { Flag, Int, diff --git a/ChocolArm64/State/ARoundMode.cs b/ChocolArm64/State/RoundMode.cs similarity index 86% rename from ChocolArm64/State/ARoundMode.cs rename to ChocolArm64/State/RoundMode.cs index 9896f3075e..b687cc8e9f 100644 --- a/ChocolArm64/State/ARoundMode.cs +++ b/ChocolArm64/State/RoundMode.cs @@ -1,10 +1,10 @@ namespace ChocolArm64.State { - public enum ARoundMode + enum RoundMode { ToNearest = 0, TowardsPlusInfinity = 1, TowardsMinusInfinity = 2, TowardsZero = 3 } -} \ No newline at end of file +} diff --git a/ChocolArm64/TranslatedSub.cs b/ChocolArm64/TranslatedSub.cs new file mode 100644 index 0000000000..8b3ec3f013 --- /dev/null +++ b/ChocolArm64/TranslatedSub.cs @@ -0,0 +1,150 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64 +{ + class TranslatedSub + { + private delegate long Aa64Subroutine(CpuThreadState register, MemoryManager memory); + + private const int MinCallCountForReJit = 250; + + private Aa64Subroutine _execDelegate; + + public static int StateArgIdx { get; private set; } + public static int MemoryArgIdx { get; private set; } + + public static Type[] FixedArgTypes { get; private set; } + + public DynamicMethod Method { get; private set; } + + public ReadOnlyCollection Params { get; private set; } + + private HashSet _callers; + + private TranslatedSubType _type; + + private int _callCount; + + private bool _needsReJit; + + public TranslatedSub(DynamicMethod method, List Params) + { + if (method == null) + { + throw new ArgumentNullException(nameof(method)); + } + + if (Params == null) + { + throw new ArgumentNullException(nameof(Params)); + } + + Method = method; + this.Params = Params.AsReadOnly(); + + _callers = new HashSet(); + + PrepareDelegate(); + } + + static TranslatedSub() + { + MethodInfo mthdInfo = typeof(Aa64Subroutine).GetMethod("Invoke"); + + ParameterInfo[] Params = mthdInfo.GetParameters(); + + FixedArgTypes = new Type[Params.Length]; + + for (int index = 0; index < Params.Length; index++) + { + Type paramType = Params[index].ParameterType; + + FixedArgTypes[index] = paramType; + + if (paramType == typeof(CpuThreadState)) + { + StateArgIdx = index; + } + else if (paramType == typeof(MemoryManager)) + { + MemoryArgIdx = index; + } + } + } + + private void PrepareDelegate() + { + string name = $"{Method.Name}_Dispatch"; + + DynamicMethod mthd = new DynamicMethod(name, typeof(long), FixedArgTypes); + + ILGenerator generator = mthd.GetILGenerator(); + + generator.EmitLdargSeq(FixedArgTypes.Length); + + foreach (Register reg in Params) + { + generator.EmitLdarg(StateArgIdx); + + generator.Emit(OpCodes.Ldfld, reg.GetField()); + } + + generator.Emit(OpCodes.Call, Method); + generator.Emit(OpCodes.Ret); + + _execDelegate = (Aa64Subroutine)mthd.CreateDelegate(typeof(Aa64Subroutine)); + } + + public bool ShouldReJit() + { + if (_needsReJit && _callCount < MinCallCountForReJit) + { + _callCount++; + + return false; + } + + return _needsReJit; + } + + public long Execute(CpuThreadState threadState, MemoryManager memory) + { + return _execDelegate(threadState, memory); + } + + public void AddCaller(long position) + { + lock (_callers) + { + _callers.Add(position); + } + } + + public long[] GetCallerPositions() + { + lock (_callers) + { + return _callers.ToArray(); + } + } + + public void SetType(TranslatedSubType type) + { + _type = type; + + if (type == TranslatedSubType.SubTier0) + { + _needsReJit = true; + } + } + + public void MarkForReJit() => _needsReJit = true; + } +} \ No newline at end of file diff --git a/ChocolArm64/ATranslatedSubType.cs b/ChocolArm64/TranslatedSubType.cs similarity index 72% rename from ChocolArm64/ATranslatedSubType.cs rename to ChocolArm64/TranslatedSubType.cs index 14893abdfa..f57aea945d 100644 --- a/ChocolArm64/ATranslatedSubType.cs +++ b/ChocolArm64/TranslatedSubType.cs @@ -1,6 +1,6 @@ namespace ChocolArm64 { - enum ATranslatedSubType + enum TranslatedSubType { SubTier0, SubTier1 diff --git a/ChocolArm64/Translation/AILBarrier.cs b/ChocolArm64/Translation/AILBarrier.cs deleted file mode 100644 index 25b08de31d..0000000000 --- a/ChocolArm64/Translation/AILBarrier.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Translation -{ - struct AILBarrier : IAILEmit - { - public void Emit(AILEmitter Context) { } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILBlock.cs b/ChocolArm64/Translation/AILBlock.cs deleted file mode 100644 index e580e09c9b..0000000000 --- a/ChocolArm64/Translation/AILBlock.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections.Generic; - -namespace ChocolArm64.Translation -{ - class AILBlock : IAILEmit - { - public long IntInputs { get; private set; } - public long IntOutputs { get; private set; } - public long IntAwOutputs { get; private set; } - - public long VecInputs { get; private set; } - public long VecOutputs { get; private set; } - public long VecAwOutputs { get; private set; } - - public bool HasStateStore { get; private set; } - - public List ILEmitters { get; private set; } - - public AILBlock Next { get; set; } - public AILBlock Branch { get; set; } - - public AILBlock() - { - ILEmitters = new List(); - } - - public void Add(IAILEmit ILEmitter) - { - if (ILEmitter is AILBarrier) - { - //Those barriers are used to separate the groups of CIL - //opcodes emitted by each ARM instruction. - //We can only consider the new outputs for doing input elimination - //after all the CIL opcodes used by the instruction being emitted. - IntAwOutputs = IntOutputs; - VecAwOutputs = VecOutputs; - } - else if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index)) - { - switch (Ld.IoType) - { - case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntAwOutputs; break; - case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntAwOutputs; break; - case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecAwOutputs; break; - } - } - else if (ILEmitter is AILOpCodeStore St) - { - if (AILEmitter.IsRegIndex(St.Index)) - { - switch (St.IoType) - { - case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break; - case AIoType.Int: IntOutputs |= 1L << St.Index; break; - case AIoType.Vector: VecOutputs |= 1L << St.Index; break; - } - } - - if (St.IoType == AIoType.Fields) - { - HasStateStore = true; - } - } - - ILEmitters.Add(ILEmitter); - } - - public void Emit(AILEmitter Context) - { - foreach (IAILEmit ILEmitter in ILEmitters) - { - ILEmitter.Emit(Context); - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILEmitter.cs b/ChocolArm64/Translation/AILEmitter.cs deleted file mode 100644 index 8c7805353b..0000000000 --- a/ChocolArm64/Translation/AILEmitter.cs +++ /dev/null @@ -1,188 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using System; -using System.Collections.Generic; -using System.Reflection.Emit; -using System.Runtime.Intrinsics; - -namespace ChocolArm64.Translation -{ - class AILEmitter - { - public ALocalAlloc LocalAlloc { get; private set; } - - public ILGenerator Generator { get; private set; } - - private Dictionary Locals; - - private AILBlock[] ILBlocks; - - private AILBlock Root; - - private ATranslatedSub Subroutine; - - private string SubName; - - private int LocalsCount; - - public AILEmitter(ABlock[] Graph, ABlock Root, string SubName) - { - this.SubName = SubName; - - Locals = new Dictionary(); - - ILBlocks = new AILBlock[Graph.Length]; - - AILBlock GetBlock(int Index) - { - if (Index < 0 || Index >= ILBlocks.Length) - { - return null; - } - - if (ILBlocks[Index] == null) - { - ILBlocks[Index] = new AILBlock(); - } - - return ILBlocks[Index]; - } - - for (int Index = 0; Index < ILBlocks.Length; Index++) - { - AILBlock Block = GetBlock(Index); - - Block.Next = GetBlock(Array.IndexOf(Graph, Graph[Index].Next)); - Block.Branch = GetBlock(Array.IndexOf(Graph, Graph[Index].Branch)); - } - - this.Root = ILBlocks[Array.IndexOf(Graph, Root)]; - } - - public AILBlock GetILBlock(int Index) => ILBlocks[Index]; - - public ATranslatedSub GetSubroutine() - { - LocalAlloc = new ALocalAlloc(ILBlocks, Root); - - InitSubroutine(); - InitLocals(); - - foreach (AILBlock ILBlock in ILBlocks) - { - ILBlock.Emit(this); - } - - return Subroutine; - } - - private void InitSubroutine() - { - List Params = new List(); - - void SetParams(long Inputs, ARegisterType BaseType) - { - for (int Bit = 0; Bit < 64; Bit++) - { - long Mask = 1L << Bit; - - if ((Inputs & Mask) != 0) - { - Params.Add(GetRegFromBit(Bit, BaseType)); - } - } - } - - SetParams(LocalAlloc.GetIntInputs(Root), ARegisterType.Int); - SetParams(LocalAlloc.GetVecInputs(Root), ARegisterType.Vector); - - DynamicMethod Mthd = new DynamicMethod(SubName, typeof(long), GetParamTypes(Params)); - - Generator = Mthd.GetILGenerator(); - - Subroutine = new ATranslatedSub(Mthd, Params); - } - - private void InitLocals() - { - int ParamsStart = ATranslatedSub.FixedArgTypes.Length; - - Locals = new Dictionary(); - - for (int Index = 0; Index < Subroutine.Params.Count; Index++) - { - ARegister Reg = Subroutine.Params[Index]; - - Generator.EmitLdarg(Index + ParamsStart); - Generator.EmitStloc(GetLocalIndex(Reg)); - } - } - - private Type[] GetParamTypes(IList Params) - { - Type[] FixedArgs = ATranslatedSub.FixedArgTypes; - - Type[] Output = new Type[Params.Count + FixedArgs.Length]; - - FixedArgs.CopyTo(Output, 0); - - int TypeIdx = FixedArgs.Length; - - for (int Index = 0; Index < Params.Count; Index++) - { - Output[TypeIdx++] = GetFieldType(Params[Index].Type); - } - - return Output; - } - - public int GetLocalIndex(ARegister Reg) - { - if (!Locals.TryGetValue(Reg, out int Index)) - { - Generator.DeclareLocal(GetLocalType(Reg)); - - Index = LocalsCount++; - - Locals.Add(Reg, Index); - } - - return Index; - } - - public Type GetLocalType(ARegister Reg) => GetFieldType(Reg.Type); - - public Type GetFieldType(ARegisterType RegType) - { - switch (RegType) - { - case ARegisterType.Flag: return typeof(bool); - case ARegisterType.Int: return typeof(ulong); - case ARegisterType.Vector: return typeof(Vector128); - } - - throw new ArgumentException(nameof(RegType)); - } - - public static ARegister GetRegFromBit(int Bit, ARegisterType BaseType) - { - if (Bit < 32) - { - return new ARegister(Bit, BaseType); - } - else if (BaseType == ARegisterType.Int) - { - return new ARegister(Bit & 0x1f, ARegisterType.Flag); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Bit)); - } - } - - public static bool IsRegIndex(int Index) - { - return Index >= 0 && Index < 32; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs deleted file mode 100644 index 40e33ba8ea..0000000000 --- a/ChocolArm64/Translation/AILEmitterCtx.cs +++ /dev/null @@ -1,558 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Instruction; -using ChocolArm64.State; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - class AILEmitterCtx - { - private ATranslator Translator; - - private Dictionary Labels; - - private int BlkIndex; - private int OpcIndex; - - private ABlock[] Graph; - private ABlock Root; - public ABlock CurrBlock => Graph[BlkIndex]; - public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex]; - - private AILEmitter Emitter; - - private AILBlock ILBlock; - - private AOpCode OptOpLastCompare; - private AOpCode OptOpLastFlagSet; - - //This is the index of the temporary register, used to store temporary - //values needed by some functions, since IL doesn't have a swap instruction. - //You can use any value here as long it doesn't conflict with the indices - //for the other registers. Any value >= 64 or < 0 will do. - private const int Tmp1Index = -1; - private const int Tmp2Index = -2; - private const int Tmp3Index = -3; - private const int Tmp4Index = -4; - private const int Tmp5Index = -5; - - public AILEmitterCtx( - ATranslator Translator, - ABlock[] Graph, - ABlock Root, - string SubName) - { - if (Translator == null) - { - throw new ArgumentNullException(nameof(Translator)); - } - - if (Graph == null) - { - throw new ArgumentNullException(nameof(Graph)); - } - - if (Root == null) - { - throw new ArgumentNullException(nameof(Root)); - } - - this.Translator = Translator; - this.Graph = Graph; - this.Root = Root; - - Labels = new Dictionary(); - - Emitter = new AILEmitter(Graph, Root, SubName); - - ILBlock = Emitter.GetILBlock(0); - - OpcIndex = -1; - - if (Graph.Length == 0 || !AdvanceOpCode()) - { - throw new ArgumentException(nameof(Graph)); - } - } - - public ATranslatedSub GetSubroutine() - { - return Emitter.GetSubroutine(); - } - - public bool AdvanceOpCode() - { - if (OpcIndex + 1 == CurrBlock.OpCodes.Count && - BlkIndex + 1 == Graph.Length) - { - return false; - } - - while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) - { - BlkIndex++; - OpcIndex = -1; - - OptOpLastFlagSet = null; - OptOpLastCompare = null; - - ILBlock = Emitter.GetILBlock(BlkIndex); - } - - return true; - } - - public void EmitOpCode() - { - if (OpcIndex == 0) - { - MarkLabel(GetLabel(CurrBlock.Position)); - - EmitSynchronization(); - } - - CurrOp.Emitter(this); - - ILBlock.Add(new AILBarrier()); - } - - private void EmitSynchronization() - { - EmitLdarg(ATranslatedSub.StateArgIdx); - - EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.Synchronize)); - - EmitLdc_I4(0); - - AILLabel LblContinue = new AILLabel(); - - Emit(OpCodes.Bne_Un_S, LblContinue); - - EmitLdc_I8(0); - - Emit(OpCodes.Ret); - - MarkLabel(LblContinue); - } - - public bool TryOptEmitSubroutineCall() - { - if (CurrBlock.Next == null) - { - return false; - } - - if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub)) - { - return false; - } - - for (int Index = 0; Index < ATranslatedSub.FixedArgTypes.Length; Index++) - { - EmitLdarg(Index); - } - - foreach (ARegister Reg in Sub.Params) - { - switch (Reg.Type) - { - case ARegisterType.Flag: Ldloc(Reg.Index, AIoType.Flag); break; - case ARegisterType.Int: Ldloc(Reg.Index, AIoType.Int); break; - case ARegisterType.Vector: Ldloc(Reg.Index, AIoType.Vector); break; - } - } - - EmitCall(Sub.Method); - - Sub.AddCaller(Root.Position); - - return true; - } - - public void TryOptMarkCondWithoutCmp() - { - OptOpLastCompare = CurrOp; - - AInstEmitAluHelper.EmitDataLoadOpers(this); - - Stloc(Tmp4Index, AIoType.Int); - Stloc(Tmp3Index, AIoType.Int); - } - - private Dictionary BranchOps = new Dictionary() - { - { ACond.Eq, OpCodes.Beq }, - { ACond.Ne, OpCodes.Bne_Un }, - { ACond.Ge_Un, OpCodes.Bge_Un }, - { ACond.Lt_Un, OpCodes.Blt_Un }, - { ACond.Gt_Un, OpCodes.Bgt_Un }, - { ACond.Le_Un, OpCodes.Ble_Un }, - { ACond.Ge, OpCodes.Bge }, - { ACond.Lt, OpCodes.Blt }, - { ACond.Gt, OpCodes.Bgt }, - { ACond.Le, OpCodes.Ble } - }; - - public void EmitCondBranch(AILLabel Target, ACond Cond) - { - OpCode ILOp; - - int IntCond = (int)Cond; - - if (OptOpLastCompare != null && - OptOpLastCompare == OptOpLastFlagSet && BranchOps.ContainsKey(Cond)) - { - Ldloc(Tmp3Index, AIoType.Int, OptOpLastCompare.RegisterSize); - Ldloc(Tmp4Index, AIoType.Int, OptOpLastCompare.RegisterSize); - - ILOp = BranchOps[Cond]; - } - else if (IntCond < 14) - { - int CondTrue = IntCond >> 1; - - switch (CondTrue) - { - case 0: EmitLdflg((int)APState.ZBit); break; - case 1: EmitLdflg((int)APState.CBit); break; - case 2: EmitLdflg((int)APState.NBit); break; - case 3: EmitLdflg((int)APState.VBit); break; - - case 4: - EmitLdflg((int)APState.CBit); - EmitLdflg((int)APState.ZBit); - - Emit(OpCodes.Not); - Emit(OpCodes.And); - break; - - case 5: - case 6: - EmitLdflg((int)APState.NBit); - EmitLdflg((int)APState.VBit); - - Emit(OpCodes.Ceq); - - if (CondTrue == 6) - { - EmitLdflg((int)APState.ZBit); - - Emit(OpCodes.Not); - Emit(OpCodes.And); - } - break; - } - - ILOp = (IntCond & 1) != 0 - ? OpCodes.Brfalse - : OpCodes.Brtrue; - } - else - { - ILOp = OpCodes.Br; - } - - Emit(ILOp, Target); - } - - public void EmitCast(AIntType IntType) - { - switch (IntType) - { - case AIntType.UInt8: Emit(OpCodes.Conv_U1); break; - case AIntType.UInt16: Emit(OpCodes.Conv_U2); break; - case AIntType.UInt32: Emit(OpCodes.Conv_U4); break; - case AIntType.UInt64: Emit(OpCodes.Conv_U8); break; - case AIntType.Int8: Emit(OpCodes.Conv_I1); break; - case AIntType.Int16: Emit(OpCodes.Conv_I2); break; - case AIntType.Int32: Emit(OpCodes.Conv_I4); break; - case AIntType.Int64: Emit(OpCodes.Conv_I8); break; - } - - bool Sz64 = CurrOp.RegisterSize != ARegisterSize.Int32; - - if (Sz64 == (IntType == AIntType.UInt64 || - IntType == AIntType.Int64)) - { - return; - } - - if (Sz64) - { - Emit(IntType >= AIntType.Int8 - ? OpCodes.Conv_I8 - : OpCodes.Conv_U8); - } - else - { - Emit(OpCodes.Conv_U4); - } - } - - public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl); - public void EmitLsr(int Amount) => EmitILShift(Amount, OpCodes.Shr_Un); - public void EmitAsr(int Amount) => EmitILShift(Amount, OpCodes.Shr); - - private void EmitILShift(int Amount, OpCode ILOp) - { - if (Amount > 0) - { - EmitLdc_I4(Amount); - - Emit(ILOp); - } - } - - public void EmitRor(int Amount) - { - if (Amount > 0) - { - Stloc(Tmp2Index, AIoType.Int); - Ldloc(Tmp2Index, AIoType.Int); - - EmitLdc_I4(Amount); - - Emit(OpCodes.Shr_Un); - - Ldloc(Tmp2Index, AIoType.Int); - - EmitLdc_I4(CurrOp.GetBitsCount() - Amount); - - Emit(OpCodes.Shl); - Emit(OpCodes.Or); - } - } - - public AILLabel GetLabel(long Position) - { - if (!Labels.TryGetValue(Position, out AILLabel Output)) - { - Output = new AILLabel(); - - Labels.Add(Position, Output); - } - - return Output; - } - - public void MarkLabel(AILLabel Label) - { - ILBlock.Add(Label); - } - - public void Emit(OpCode ILOp) - { - ILBlock.Add(new AILOpCode(ILOp)); - } - - public void Emit(OpCode ILOp, AILLabel Label) - { - ILBlock.Add(new AILOpCodeBranch(ILOp, Label)); - } - - public void Emit(string Text) - { - ILBlock.Add(new AILOpCodeLog(Text)); - } - - public void EmitLdarg(int Index) - { - ILBlock.Add(new AILOpCodeLoad(Index, AIoType.Arg)); - } - - public void EmitLdintzr(int Index) - { - if (Index != AThreadState.ZRIndex) - { - EmitLdint(Index); - } - else - { - EmitLdc_I(0); - } - } - - public void EmitStintzr(int Index) - { - if (Index != AThreadState.ZRIndex) - { - EmitStint(Index); - } - else - { - Emit(OpCodes.Pop); - } - } - - public void EmitLoadState(ABlock RetBlk) - { - ILBlock.Add(new AILOpCodeLoad(Array.IndexOf(Graph, RetBlk), AIoType.Fields)); - } - - public void EmitStoreState() - { - ILBlock.Add(new AILOpCodeStore(Array.IndexOf(Graph, CurrBlock), AIoType.Fields)); - } - - public void EmitLdtmp() => EmitLdint(Tmp1Index); - public void EmitSttmp() => EmitStint(Tmp1Index); - - public void EmitLdvectmp() => EmitLdvec(Tmp5Index); - public void EmitStvectmp() => EmitStvec(Tmp5Index); - - public void EmitLdint(int Index) => Ldloc(Index, AIoType.Int); - public void EmitStint(int Index) => Stloc(Index, AIoType.Int); - - public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector); - public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector); - - public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag); - public void EmitStflg(int Index) - { - OptOpLastFlagSet = CurrOp; - - Stloc(Index, AIoType.Flag); - } - - private void Ldloc(int Index, AIoType IoType) - { - ILBlock.Add(new AILOpCodeLoad(Index, IoType, CurrOp.RegisterSize)); - } - - private void Ldloc(int Index, AIoType IoType, ARegisterSize RegisterSize) - { - ILBlock.Add(new AILOpCodeLoad(Index, IoType, RegisterSize)); - } - - private void Stloc(int Index, AIoType IoType) - { - ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize)); - } - - public void EmitCallPropGet(Type ObjType, string PropName) - { - if (ObjType == null) - { - throw new ArgumentNullException(nameof(ObjType)); - } - - if (PropName == null) - { - throw new ArgumentNullException(nameof(PropName)); - } - - EmitCall(ObjType.GetMethod($"get_{PropName}")); - } - - public void EmitCallPropSet(Type ObjType, string PropName) - { - if (ObjType == null) - { - throw new ArgumentNullException(nameof(ObjType)); - } - - if (PropName == null) - { - throw new ArgumentNullException(nameof(PropName)); - } - - EmitCall(ObjType.GetMethod($"set_{PropName}")); - } - - public void EmitCall(Type ObjType, string MthdName) - { - if (ObjType == null) - { - throw new ArgumentNullException(nameof(ObjType)); - } - - if (MthdName == null) - { - throw new ArgumentNullException(nameof(MthdName)); - } - - EmitCall(ObjType.GetMethod(MthdName)); - } - - public void EmitPrivateCall(Type ObjType, string MthdName) - { - if (ObjType == null) - { - throw new ArgumentNullException(nameof(ObjType)); - } - - if (MthdName == null) - { - throw new ArgumentNullException(nameof(MthdName)); - } - - EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic)); - } - - public void EmitCall(MethodInfo MthdInfo) - { - if (MthdInfo == null) - { - throw new ArgumentNullException(nameof(MthdInfo)); - } - - ILBlock.Add(new AILOpCodeCall(MthdInfo)); - } - - public void EmitLdc_I(long Value) - { - if (CurrOp.RegisterSize == ARegisterSize.Int32) - { - EmitLdc_I4((int)Value); - } - else - { - EmitLdc_I8(Value); - } - } - - public void EmitLdc_I4(int Value) - { - ILBlock.Add(new AILOpCodeConst(Value)); - } - - public void EmitLdc_I8(long Value) - { - ILBlock.Add(new AILOpCodeConst(Value)); - } - - public void EmitLdc_R4(float Value) - { - ILBlock.Add(new AILOpCodeConst(Value)); - } - - public void EmitLdc_R8(double Value) - { - ILBlock.Add(new AILOpCodeConst(Value)); - } - - public void EmitZNFlagCheck() - { - EmitZNCheck(OpCodes.Ceq, (int)APState.ZBit); - EmitZNCheck(OpCodes.Clt, (int)APState.NBit); - } - - private void EmitZNCheck(OpCode ILCmpOp, int Flag) - { - Emit(OpCodes.Dup); - Emit(OpCodes.Ldc_I4_0); - - if (CurrOp.RegisterSize != ARegisterSize.Int32) - { - Emit(OpCodes.Conv_I8); - } - - Emit(ILCmpOp); - - EmitStflg(Flag); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILLabel.cs b/ChocolArm64/Translation/AILLabel.cs deleted file mode 100644 index 0ee39ad7e2..0000000000 --- a/ChocolArm64/Translation/AILLabel.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - class AILLabel : IAILEmit - { - private bool HasLabel; - - private Label Lbl; - - public void Emit(AILEmitter Context) - { - Context.Generator.MarkLabel(GetLabel(Context)); - } - - public Label GetLabel(AILEmitter Context) - { - if (!HasLabel) - { - Lbl = Context.Generator.DefineLabel(); - - HasLabel = true; - } - - return Lbl; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILOpCode.cs b/ChocolArm64/Translation/AILOpCode.cs deleted file mode 100644 index a4bc93a065..0000000000 --- a/ChocolArm64/Translation/AILOpCode.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCode : IAILEmit - { - private OpCode ILOp; - - public AILOpCode(OpCode ILOp) - { - this.ILOp = ILOp; - } - - public void Emit(AILEmitter Context) - { - Context.Generator.Emit(ILOp); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILOpCodeBranch.cs b/ChocolArm64/Translation/AILOpCodeBranch.cs deleted file mode 100644 index e4caad1ffa..0000000000 --- a/ChocolArm64/Translation/AILOpCodeBranch.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCodeBranch : IAILEmit - { - private OpCode ILOp; - private AILLabel Label; - - public AILOpCodeBranch(OpCode ILOp, AILLabel Label) - { - this.ILOp = ILOp; - this.Label = Label; - } - - public void Emit(AILEmitter Context) - { - Context.Generator.Emit(ILOp, Label.GetLabel(Context)); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILOpCodeCall.cs b/ChocolArm64/Translation/AILOpCodeCall.cs deleted file mode 100644 index 8cd944eb01..0000000000 --- a/ChocolArm64/Translation/AILOpCodeCall.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCodeCall : IAILEmit - { - private MethodInfo MthdInfo; - - public AILOpCodeCall(MethodInfo MthdInfo) - { - this.MthdInfo = MthdInfo; - } - - public void Emit(AILEmitter Context) - { - Context.Generator.Emit(OpCodes.Call, MthdInfo); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILOpCodeConst.cs b/ChocolArm64/Translation/AILOpCodeConst.cs deleted file mode 100644 index fee8640768..0000000000 --- a/ChocolArm64/Translation/AILOpCodeConst.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Reflection.Emit; -using System.Runtime.InteropServices; - -namespace ChocolArm64.Translation -{ - class AILOpCodeConst : IAILEmit - { - [StructLayout(LayoutKind.Explicit, Size = 8)] - private struct ImmVal - { - [FieldOffset(0)] public int I4; - [FieldOffset(0)] public long I8; - [FieldOffset(0)] public float R4; - [FieldOffset(0)] public double R8; - } - - private ImmVal Value; - - private enum ConstType - { - Int32, - Int64, - Single, - Double - } - - private ConstType Type; - - private AILOpCodeConst(ConstType Type) - { - this.Type = Type; - } - - public AILOpCodeConst(int Value) : this(ConstType.Int32) - { - this.Value = new ImmVal { I4 = Value }; - } - - public AILOpCodeConst(long Value) : this(ConstType.Int64) - { - this.Value = new ImmVal { I8 = Value }; - } - - public AILOpCodeConst(float Value) : this(ConstType.Single) - { - this.Value = new ImmVal { R4 = Value }; - } - - public AILOpCodeConst(double Value) : this(ConstType.Double) - { - this.Value = new ImmVal { R8 = Value }; - } - - public void Emit(AILEmitter Context) - { - switch (Type) - { - case ConstType.Int32: Context.Generator.EmitLdc_I4(Value.I4); break; - case ConstType.Int64: Context.Generator.Emit(OpCodes.Ldc_I8, Value.I8); break; - case ConstType.Single: Context.Generator.Emit(OpCodes.Ldc_R4, Value.R4); break; - case ConstType.Double: Context.Generator.Emit(OpCodes.Ldc_R8, Value.R8); break; - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILOpCodeLoad.cs b/ChocolArm64/Translation/AILOpCodeLoad.cs deleted file mode 100644 index d60ce539f7..0000000000 --- a/ChocolArm64/Translation/AILOpCodeLoad.cs +++ /dev/null @@ -1,75 +0,0 @@ -using ChocolArm64.State; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCodeLoad : IAILEmit - { - public int Index { get; private set; } - - public AIoType IoType { get; private set; } - - public ARegisterSize RegisterSize { get; private set; } - - public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize = 0) - { - this.Index = Index; - this.IoType = IoType; - this.RegisterSize = RegisterSize; - } - - public void Emit(AILEmitter Context) - { - switch (IoType) - { - case AIoType.Arg: Context.Generator.EmitLdarg(Index); break; - - case AIoType.Fields: - { - long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index)); - long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index)); - - LoadLocals(Context, IntInputs, ARegisterType.Int); - LoadLocals(Context, VecInputs, ARegisterType.Vector); - - break; - } - - case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break; - case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break; - case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break; - } - } - - private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType) - { - for (int Bit = 0; Bit < 64; Bit++) - { - long Mask = 1L << Bit; - - if ((Inputs & Mask) != 0) - { - ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType); - - Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField()); - - Context.Generator.EmitStloc(Context.GetLocalIndex(Reg)); - } - } - } - - private void EmitLdloc(AILEmitter Context, int Index, ARegisterType RegisterType) - { - ARegister Reg = new ARegister(Index, RegisterType); - - Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg)); - - if (RegisterType == ARegisterType.Int && - RegisterSize == ARegisterSize.Int32) - { - Context.Generator.Emit(OpCodes.Conv_U4); - } - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILOpCodeLog.cs b/ChocolArm64/Translation/AILOpCodeLog.cs deleted file mode 100644 index 1338ca1f3a..0000000000 --- a/ChocolArm64/Translation/AILOpCodeLog.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace ChocolArm64.Translation -{ - struct AILOpCodeLog : IAILEmit - { - private string Text; - - public AILOpCodeLog(string Text) - { - this.Text = Text; - } - - public void Emit(AILEmitter Context) - { - Context.Generator.EmitWriteLine(Text); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/AILOpCodeStore.cs b/ChocolArm64/Translation/AILOpCodeStore.cs deleted file mode 100644 index a0feb43773..0000000000 --- a/ChocolArm64/Translation/AILOpCodeStore.cs +++ /dev/null @@ -1,75 +0,0 @@ -using ChocolArm64.State; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - struct AILOpCodeStore : IAILEmit - { - public int Index { get; private set; } - - public AIoType IoType { get; private set; } - - public ARegisterSize RegisterSize { get; private set; } - - public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize = 0) - { - this.Index = Index; - this.IoType = IoType; - this.RegisterSize = RegisterSize; - } - - public void Emit(AILEmitter Context) - { - switch (IoType) - { - case AIoType.Arg: Context.Generator.EmitStarg(Index); break; - - case AIoType.Fields: - { - long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index)); - long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index)); - - StoreLocals(Context, IntOutputs, ARegisterType.Int); - StoreLocals(Context, VecOutputs, ARegisterType.Vector); - - break; - } - - case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break; - case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break; - case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break; - } - } - - private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType) - { - for (int Bit = 0; Bit < 64; Bit++) - { - long Mask = 1L << Bit; - - if ((Outputs & Mask) != 0) - { - ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType); - - Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg)); - - Context.Generator.Emit(OpCodes.Stfld, Reg.GetField()); - } - } - } - - private void EmitStloc(AILEmitter Context, int Index, ARegisterType RegisterType) - { - ARegister Reg = new ARegister(Index, RegisterType); - - if (RegisterType == ARegisterType.Int && - RegisterSize == ARegisterSize.Int32) - { - Context.Generator.Emit(OpCodes.Conv_U8); - } - - Context.Generator.EmitStloc(Context.GetLocalIndex(Reg)); - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/ALocalAlloc.cs b/ChocolArm64/Translation/ALocalAlloc.cs deleted file mode 100644 index 8e9047804d..0000000000 --- a/ChocolArm64/Translation/ALocalAlloc.cs +++ /dev/null @@ -1,229 +0,0 @@ -using System.Collections.Generic; - -namespace ChocolArm64.Translation -{ - class ALocalAlloc - { - private class PathIo - { - private Dictionary AllInputs; - private Dictionary CmnOutputs; - - private long AllOutputs; - - public PathIo() - { - AllInputs = new Dictionary(); - CmnOutputs = new Dictionary(); - } - - public PathIo(AILBlock Root, long Inputs, long Outputs) : this() - { - Set(Root, Inputs, Outputs); - } - - public void Set(AILBlock Root, long Inputs, long Outputs) - { - if (!AllInputs.TryAdd(Root, Inputs)) - { - AllInputs[Root] |= Inputs; - } - - if (!CmnOutputs.TryAdd(Root, Outputs)) - { - CmnOutputs[Root] &= Outputs; - } - - AllOutputs |= Outputs; - } - - public long GetInputs(AILBlock Root) - { - if (AllInputs.TryGetValue(Root, out long Inputs)) - { - return Inputs | (AllOutputs & ~CmnOutputs[Root]); - } - - return 0; - } - - public long GetOutputs() - { - return AllOutputs; - } - } - - private Dictionary IntPaths; - private Dictionary VecPaths; - - private struct BlockIo - { - public AILBlock Block; - public AILBlock Entry; - - public long IntInputs; - public long VecInputs; - public long IntOutputs; - public long VecOutputs; - } - - private const int MaxOptGraphLength = 40; - - public ALocalAlloc(AILBlock[] Graph, AILBlock Root) - { - IntPaths = new Dictionary(); - VecPaths = new Dictionary(); - - if (Graph.Length > 1 && - Graph.Length < MaxOptGraphLength) - { - InitializeOptimal(Graph, Root); - } - else - { - InitializeFast(Graph); - } - } - - private void InitializeOptimal(AILBlock[] Graph, AILBlock Root) - { - //This will go through all possible paths on the graph, - //and store all inputs/outputs for each block. A register - //that was previously written to already is not considered an input. - //When a block can be reached by more than one path, then the - //output from all paths needs to be set for this block, and - //only outputs present in all of the parent blocks can be considered - //when doing input elimination. Each block chain have a root, that's where - //the code starts executing. They are present on the subroutine start point, - //and on call return points too (address written to X30 by BL). - HashSet Visited = new HashSet(); - - Queue Unvisited = new Queue(); - - void Enqueue(BlockIo Block) - { - if (!Visited.Contains(Block)) - { - Unvisited.Enqueue(Block); - - Visited.Add(Block); - } - } - - Enqueue(new BlockIo() - { - Block = Root, - Entry = Root - }); - - while (Unvisited.Count > 0) - { - BlockIo Current = Unvisited.Dequeue(); - - Current.IntInputs |= Current.Block.IntInputs & ~Current.IntOutputs; - Current.VecInputs |= Current.Block.VecInputs & ~Current.VecOutputs; - Current.IntOutputs |= Current.Block.IntOutputs; - Current.VecOutputs |= Current.Block.VecOutputs; - - //Check if this is a exit block - //(a block that returns or calls another sub). - if ((Current.Block.Next == null && - Current.Block.Branch == null) || Current.Block.HasStateStore) - { - if (!IntPaths.TryGetValue(Current.Block, out PathIo IntPath)) - { - IntPaths.Add(Current.Block, IntPath = new PathIo()); - } - - if (!VecPaths.TryGetValue(Current.Block, out PathIo VecPath)) - { - VecPaths.Add(Current.Block, VecPath = new PathIo()); - } - - IntPath.Set(Current.Entry, Current.IntInputs, Current.IntOutputs); - VecPath.Set(Current.Entry, Current.VecInputs, Current.VecOutputs); - } - - void EnqueueFromCurrent(AILBlock Block, bool RetTarget) - { - BlockIo BlkIO = new BlockIo() { Block = Block }; - - if (RetTarget) - { - BlkIO.Entry = Block; - } - else - { - BlkIO.Entry = Current.Entry; - BlkIO.IntInputs = Current.IntInputs; - BlkIO.VecInputs = Current.VecInputs; - BlkIO.IntOutputs = Current.IntOutputs; - BlkIO.VecOutputs = Current.VecOutputs; - } - - Enqueue(BlkIO); - } - - if (Current.Block.Next != null) - { - EnqueueFromCurrent(Current.Block.Next, Current.Block.HasStateStore); - } - - if (Current.Block.Branch != null) - { - EnqueueFromCurrent(Current.Block.Branch, false); - } - } - } - - private void InitializeFast(AILBlock[] Graph) - { - //This is WAY faster than InitializeOptimal, but results in - //uneeded loads and stores, so the resulting code will be slower. - long IntInputs = 0, IntOutputs = 0; - long VecInputs = 0, VecOutputs = 0; - - foreach (AILBlock Block in Graph) - { - IntInputs |= Block.IntInputs; - IntOutputs |= Block.IntOutputs; - VecInputs |= Block.VecInputs; - VecOutputs |= Block.VecOutputs; - } - - //It's possible that not all code paths writes to those output registers, - //in those cases if we attempt to write an output registers that was - //not written, we will be just writing zero and messing up the old register value. - //So we just need to ensure that all outputs are loaded. - if (Graph.Length > 1) - { - IntInputs |= IntOutputs; - VecInputs |= VecOutputs; - } - - foreach (AILBlock Block in Graph) - { - IntPaths.Add(Block, new PathIo(Block, IntInputs, IntOutputs)); - VecPaths.Add(Block, new PathIo(Block, VecInputs, VecOutputs)); - } - } - - public long GetIntInputs(AILBlock Root) => GetInputsImpl(Root, IntPaths.Values); - public long GetVecInputs(AILBlock Root) => GetInputsImpl(Root, VecPaths.Values); - - private long GetInputsImpl(AILBlock Root, IEnumerable Values) - { - long Inputs = 0; - - foreach (PathIo Path in Values) - { - Inputs |= Path.GetInputs(Root); - } - - return Inputs; - } - - public long GetIntOutputs(AILBlock Block) => IntPaths[Block].GetOutputs(); - public long GetVecOutputs(AILBlock Block) => VecPaths[Block].GetOutputs(); - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/IAILEmit.cs b/ChocolArm64/Translation/IAILEmit.cs deleted file mode 100644 index 6e4e9a7855..0000000000 --- a/ChocolArm64/Translation/IAILEmit.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ChocolArm64.Translation -{ - interface IAILEmit - { - void Emit(AILEmitter Context); - } -} \ No newline at end of file diff --git a/ChocolArm64/Translation/IILEmit.cs b/ChocolArm64/Translation/IILEmit.cs new file mode 100644 index 0000000000..3c3925ee0a --- /dev/null +++ b/ChocolArm64/Translation/IILEmit.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Translation +{ + interface IILEmit + { + void Emit(ILEmitter context); + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILBarrier.cs b/ChocolArm64/Translation/ILBarrier.cs new file mode 100644 index 0000000000..f931e9922d --- /dev/null +++ b/ChocolArm64/Translation/ILBarrier.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Translation +{ + struct ILBarrier : IILEmit + { + public void Emit(ILEmitter context) { } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILBlock.cs b/ChocolArm64/Translation/ILBlock.cs new file mode 100644 index 0000000000..d51e8d9ec3 --- /dev/null +++ b/ChocolArm64/Translation/ILBlock.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Translation +{ + class ILBlock : IILEmit + { + public long IntInputs { get; private set; } + public long IntOutputs { get; private set; } + public long IntAwOutputs { get; private set; } + + public long VecInputs { get; private set; } + public long VecOutputs { get; private set; } + public long VecAwOutputs { get; private set; } + + public bool HasStateStore { get; private set; } + + public List IlEmitters { get; private set; } + + public ILBlock Next { get; set; } + public ILBlock Branch { get; set; } + + public ILBlock() + { + IlEmitters = new List(); + } + + public void Add(IILEmit ilEmitter) + { + if (ilEmitter is ILBarrier) + { + //Those barriers are used to separate the groups of CIL + //opcodes emitted by each ARM instruction. + //We can only consider the new outputs for doing input elimination + //after all the CIL opcodes used by the instruction being emitted. + IntAwOutputs = IntOutputs; + VecAwOutputs = VecOutputs; + } + else if (ilEmitter is IlOpCodeLoad ld && ILEmitter.IsRegIndex(ld.Index)) + { + switch (ld.IoType) + { + case IoType.Flag: IntInputs |= ((1L << ld.Index) << 32) & ~IntAwOutputs; break; + case IoType.Int: IntInputs |= (1L << ld.Index) & ~IntAwOutputs; break; + case IoType.Vector: VecInputs |= (1L << ld.Index) & ~VecAwOutputs; break; + } + } + else if (ilEmitter is IlOpCodeStore st) + { + if (ILEmitter.IsRegIndex(st.Index)) + { + switch (st.IoType) + { + case IoType.Flag: IntOutputs |= (1L << st.Index) << 32; break; + case IoType.Int: IntOutputs |= 1L << st.Index; break; + case IoType.Vector: VecOutputs |= 1L << st.Index; break; + } + } + + if (st.IoType == IoType.Fields) + { + HasStateStore = true; + } + } + + IlEmitters.Add(ilEmitter); + } + + public void Emit(ILEmitter context) + { + foreach (IILEmit ilEmitter in IlEmitters) + { + ilEmitter.Emit(context); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILEmitter.cs b/ChocolArm64/Translation/ILEmitter.cs new file mode 100644 index 0000000000..543528d716 --- /dev/null +++ b/ChocolArm64/Translation/ILEmitter.cs @@ -0,0 +1,188 @@ +using ChocolArm64.Decoders; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Reflection.Emit; +using System.Runtime.Intrinsics; + +namespace ChocolArm64.Translation +{ + class ILEmitter + { + public LocalAlloc LocalAlloc { get; private set; } + + public ILGenerator Generator { get; private set; } + + private Dictionary _locals; + + private ILBlock[] _ilBlocks; + + private ILBlock _root; + + private TranslatedSub _subroutine; + + private string _subName; + + private int _localsCount; + + public ILEmitter(Block[] graph, Block root, string subName) + { + _subName = subName; + + _locals = new Dictionary(); + + _ilBlocks = new ILBlock[graph.Length]; + + ILBlock GetBlock(int index) + { + if (index < 0 || index >= _ilBlocks.Length) + { + return null; + } + + if (_ilBlocks[index] == null) + { + _ilBlocks[index] = new ILBlock(); + } + + return _ilBlocks[index]; + } + + for (int index = 0; index < _ilBlocks.Length; index++) + { + ILBlock block = GetBlock(index); + + block.Next = GetBlock(Array.IndexOf(graph, graph[index].Next)); + block.Branch = GetBlock(Array.IndexOf(graph, graph[index].Branch)); + } + + _root = _ilBlocks[Array.IndexOf(graph, root)]; + } + + public ILBlock GetIlBlock(int index) => _ilBlocks[index]; + + public TranslatedSub GetSubroutine() + { + LocalAlloc = new LocalAlloc(_ilBlocks, _root); + + InitSubroutine(); + InitLocals(); + + foreach (ILBlock ilBlock in _ilBlocks) + { + ilBlock.Emit(this); + } + + return _subroutine; + } + + private void InitSubroutine() + { + List Params = new List(); + + void SetParams(long inputs, RegisterType baseType) + { + for (int bit = 0; bit < 64; bit++) + { + long mask = 1L << bit; + + if ((inputs & mask) != 0) + { + Params.Add(GetRegFromBit(bit, baseType)); + } + } + } + + SetParams(LocalAlloc.GetIntInputs(_root), RegisterType.Int); + SetParams(LocalAlloc.GetVecInputs(_root), RegisterType.Vector); + + DynamicMethod mthd = new DynamicMethod(_subName, typeof(long), GetParamTypes(Params)); + + Generator = mthd.GetILGenerator(); + + _subroutine = new TranslatedSub(mthd, Params); + } + + private void InitLocals() + { + int paramsStart = TranslatedSub.FixedArgTypes.Length; + + _locals = new Dictionary(); + + for (int index = 0; index < _subroutine.Params.Count; index++) + { + Register reg = _subroutine.Params[index]; + + Generator.EmitLdarg(index + paramsStart); + Generator.EmitStloc(GetLocalIndex(reg)); + } + } + + private Type[] GetParamTypes(IList Params) + { + Type[] fixedArgs = TranslatedSub.FixedArgTypes; + + Type[] output = new Type[Params.Count + fixedArgs.Length]; + + fixedArgs.CopyTo(output, 0); + + int typeIdx = fixedArgs.Length; + + for (int index = 0; index < Params.Count; index++) + { + output[typeIdx++] = GetFieldType(Params[index].Type); + } + + return output; + } + + public int GetLocalIndex(Register reg) + { + if (!_locals.TryGetValue(reg, out int index)) + { + Generator.DeclareLocal(GetLocalType(reg)); + + index = _localsCount++; + + _locals.Add(reg, index); + } + + return index; + } + + public Type GetLocalType(Register reg) => GetFieldType(reg.Type); + + public Type GetFieldType(RegisterType regType) + { + switch (regType) + { + case RegisterType.Flag: return typeof(bool); + case RegisterType.Int: return typeof(ulong); + case RegisterType.Vector: return typeof(Vector128); + } + + throw new ArgumentException(nameof(regType)); + } + + public static Register GetRegFromBit(int bit, RegisterType baseType) + { + if (bit < 32) + { + return new Register(bit, baseType); + } + else if (baseType == RegisterType.Int) + { + return new Register(bit & 0x1f, RegisterType.Flag); + } + else + { + throw new ArgumentOutOfRangeException(nameof(bit)); + } + } + + public static bool IsRegIndex(int index) + { + return index >= 0 && index < 32; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILEmitterCtx.cs b/ChocolArm64/Translation/ILEmitterCtx.cs new file mode 100644 index 0000000000..c1d1be366e --- /dev/null +++ b/ChocolArm64/Translation/ILEmitterCtx.cs @@ -0,0 +1,554 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Instructions; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + class ILEmitterCtx + { + private TranslatorCache _cache; + + private Dictionary _labels; + + private int _blkIndex; + private int _opcIndex; + + private Block[] _graph; + private Block _root; + public Block CurrBlock => _graph[_blkIndex]; + public OpCode64 CurrOp => _graph[_blkIndex].OpCodes[_opcIndex]; + + private ILEmitter _emitter; + + private ILBlock _ilBlock; + + private OpCode64 _optOpLastCompare; + private OpCode64 _optOpLastFlagSet; + + //This is the index of the temporary register, used to store temporary + //values needed by some functions, since IL doesn't have a swap instruction. + //You can use any value here as long it doesn't conflict with the indices + //for the other registers. Any value >= 64 or < 0 will do. + private const int Tmp1Index = -1; + private const int Tmp2Index = -2; + private const int Tmp3Index = -3; + private const int Tmp4Index = -4; + private const int Tmp5Index = -5; + private const int Tmp6Index = -6; + + public ILEmitterCtx( + TranslatorCache cache, + Block[] graph, + Block root, + string subName) + { + _cache = cache ?? throw new ArgumentNullException(nameof(cache)); + _graph = graph ?? throw new ArgumentNullException(nameof(graph)); + _root = root ?? throw new ArgumentNullException(nameof(root)); + + _labels = new Dictionary(); + + _emitter = new ILEmitter(graph, root, subName); + + _ilBlock = _emitter.GetIlBlock(0); + + _opcIndex = -1; + + if (graph.Length == 0 || !AdvanceOpCode()) + { + throw new ArgumentException(nameof(graph)); + } + } + + public TranslatedSub GetSubroutine() + { + return _emitter.GetSubroutine(); + } + + public bool AdvanceOpCode() + { + if (_opcIndex + 1 == CurrBlock.OpCodes.Count && + _blkIndex + 1 == _graph.Length) + { + return false; + } + + while (++_opcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) + { + _blkIndex++; + _opcIndex = -1; + + _optOpLastFlagSet = null; + _optOpLastCompare = null; + + _ilBlock = _emitter.GetIlBlock(_blkIndex); + } + + return true; + } + + public void EmitOpCode() + { + if (_opcIndex == 0) + { + MarkLabel(GetLabel(CurrBlock.Position)); + + EmitSynchronization(); + } + + CurrOp.Emitter(this); + + _ilBlock.Add(new ILBarrier()); + } + + private void EmitSynchronization() + { + EmitLdarg(TranslatedSub.StateArgIdx); + + EmitLdc_I4(CurrBlock.OpCodes.Count); + + EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.Synchronize)); + + EmitLdc_I4(0); + + ILLabel lblContinue = new ILLabel(); + + Emit(OpCodes.Bne_Un_S, lblContinue); + + EmitLdc_I8(0); + + Emit(OpCodes.Ret); + + MarkLabel(lblContinue); + } + + public bool TryOptEmitSubroutineCall() + { + if (CurrBlock.Next == null) + { + return false; + } + + if (CurrOp.Emitter != InstEmit.Bl) + { + return false; + } + + if (!_cache.TryGetSubroutine(((OpCodeBImmAl64)CurrOp).Imm, out TranslatedSub subroutine)) + { + return false; + } + + for (int index = 0; index < TranslatedSub.FixedArgTypes.Length; index++) + { + EmitLdarg(index); + } + + foreach (Register reg in subroutine.Params) + { + switch (reg.Type) + { + case RegisterType.Flag: Ldloc(reg.Index, IoType.Flag); break; + case RegisterType.Int: Ldloc(reg.Index, IoType.Int); break; + case RegisterType.Vector: Ldloc(reg.Index, IoType.Vector); break; + } + } + + EmitCall(subroutine.Method); + + subroutine.AddCaller(_root.Position); + + return true; + } + + public void TryOptMarkCondWithoutCmp() + { + _optOpLastCompare = CurrOp; + + InstEmitAluHelper.EmitDataLoadOpers(this); + + Stloc(Tmp4Index, IoType.Int); + Stloc(Tmp3Index, IoType.Int); + } + + private Dictionary _branchOps = new Dictionary() + { + { Cond.Eq, OpCodes.Beq }, + { Cond.Ne, OpCodes.Bne_Un }, + { Cond.GeUn, OpCodes.Bge_Un }, + { Cond.LtUn, OpCodes.Blt_Un }, + { Cond.GtUn, OpCodes.Bgt_Un }, + { Cond.LeUn, OpCodes.Ble_Un }, + { Cond.Ge, OpCodes.Bge }, + { Cond.Lt, OpCodes.Blt }, + { Cond.Gt, OpCodes.Bgt }, + { Cond.Le, OpCodes.Ble } + }; + + public void EmitCondBranch(ILLabel target, Cond cond) + { + System.Reflection.Emit.OpCode ilOp; + + int intCond = (int)cond; + + if (_optOpLastCompare != null && + _optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond)) + { + Ldloc(Tmp3Index, IoType.Int, _optOpLastCompare.RegisterSize); + Ldloc(Tmp4Index, IoType.Int, _optOpLastCompare.RegisterSize); + + ilOp = _branchOps[cond]; + } + else if (intCond < 14) + { + int condTrue = intCond >> 1; + + switch (condTrue) + { + case 0: EmitLdflg((int)PState.ZBit); break; + case 1: EmitLdflg((int)PState.CBit); break; + case 2: EmitLdflg((int)PState.NBit); break; + case 3: EmitLdflg((int)PState.VBit); break; + + case 4: + EmitLdflg((int)PState.CBit); + EmitLdflg((int)PState.ZBit); + + Emit(OpCodes.Not); + Emit(OpCodes.And); + break; + + case 5: + case 6: + EmitLdflg((int)PState.NBit); + EmitLdflg((int)PState.VBit); + + Emit(OpCodes.Ceq); + + if (condTrue == 6) + { + EmitLdflg((int)PState.ZBit); + + Emit(OpCodes.Not); + Emit(OpCodes.And); + } + break; + } + + ilOp = (intCond & 1) != 0 + ? OpCodes.Brfalse + : OpCodes.Brtrue; + } + else + { + ilOp = OpCodes.Br; + } + + Emit(ilOp, target); + } + + public void EmitCast(IntType intType) + { + switch (intType) + { + case IntType.UInt8: Emit(OpCodes.Conv_U1); break; + case IntType.UInt16: Emit(OpCodes.Conv_U2); break; + case IntType.UInt32: Emit(OpCodes.Conv_U4); break; + case IntType.UInt64: Emit(OpCodes.Conv_U8); break; + case IntType.Int8: Emit(OpCodes.Conv_I1); break; + case IntType.Int16: Emit(OpCodes.Conv_I2); break; + case IntType.Int32: Emit(OpCodes.Conv_I4); break; + case IntType.Int64: Emit(OpCodes.Conv_I8); break; + } + + bool sz64 = CurrOp.RegisterSize != RegisterSize.Int32; + + if (sz64 == (intType == IntType.UInt64 || + intType == IntType.Int64)) + { + return; + } + + if (sz64) + { + Emit(intType >= IntType.Int8 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + else + { + Emit(OpCodes.Conv_U4); + } + } + + public void EmitLsl(int amount) => EmitIlShift(amount, OpCodes.Shl); + public void EmitLsr(int amount) => EmitIlShift(amount, OpCodes.Shr_Un); + public void EmitAsr(int amount) => EmitIlShift(amount, OpCodes.Shr); + + private void EmitIlShift(int amount, System.Reflection.Emit.OpCode ilOp) + { + if (amount > 0) + { + EmitLdc_I4(amount); + + Emit(ilOp); + } + } + + public void EmitRor(int amount) + { + if (amount > 0) + { + Stloc(Tmp2Index, IoType.Int); + Ldloc(Tmp2Index, IoType.Int); + + EmitLdc_I4(amount); + + Emit(OpCodes.Shr_Un); + + Ldloc(Tmp2Index, IoType.Int); + + EmitLdc_I4(CurrOp.GetBitsCount() - amount); + + Emit(OpCodes.Shl); + Emit(OpCodes.Or); + } + } + + public ILLabel GetLabel(long position) + { + if (!_labels.TryGetValue(position, out ILLabel output)) + { + output = new ILLabel(); + + _labels.Add(position, output); + } + + return output; + } + + public void MarkLabel(ILLabel label) + { + _ilBlock.Add(label); + } + + public void Emit(System.Reflection.Emit.OpCode ilOp) + { + _ilBlock.Add(new ILOpCode(ilOp)); + } + + public void Emit(System.Reflection.Emit.OpCode ilOp, ILLabel label) + { + _ilBlock.Add(new ILOpCodeBranch(ilOp, label)); + } + + public void Emit(string text) + { + _ilBlock.Add(new ILOpCodeLog(text)); + } + + public void EmitLdarg(int index) + { + _ilBlock.Add(new IlOpCodeLoad(index, IoType.Arg)); + } + + public void EmitLdintzr(int index) + { + if (index != CpuThreadState.ZrIndex) + { + EmitLdint(index); + } + else + { + EmitLdc_I(0); + } + } + + public void EmitStintzr(int index) + { + if (index != CpuThreadState.ZrIndex) + { + EmitStint(index); + } + else + { + Emit(OpCodes.Pop); + } + } + + public void EmitLoadState(Block retBlk) + { + _ilBlock.Add(new IlOpCodeLoad(Array.IndexOf(_graph, retBlk), IoType.Fields)); + } + + public void EmitStoreState() + { + _ilBlock.Add(new IlOpCodeStore(Array.IndexOf(_graph, CurrBlock), IoType.Fields)); + } + + public void EmitLdtmp() => EmitLdint(Tmp1Index); + public void EmitSttmp() => EmitStint(Tmp1Index); + + public void EmitLdvectmp() => EmitLdvec(Tmp5Index); + public void EmitStvectmp() => EmitStvec(Tmp5Index); + + public void EmitLdvectmp2() => EmitLdvec(Tmp6Index); + public void EmitStvectmp2() => EmitStvec(Tmp6Index); + + public void EmitLdint(int index) => Ldloc(index, IoType.Int); + public void EmitStint(int index) => Stloc(index, IoType.Int); + + public void EmitLdvec(int index) => Ldloc(index, IoType.Vector); + public void EmitStvec(int index) => Stloc(index, IoType.Vector); + + public void EmitLdflg(int index) => Ldloc(index, IoType.Flag); + public void EmitStflg(int index) + { + _optOpLastFlagSet = CurrOp; + + Stloc(index, IoType.Flag); + } + + private void Ldloc(int index, IoType ioType) + { + _ilBlock.Add(new IlOpCodeLoad(index, ioType, CurrOp.RegisterSize)); + } + + private void Ldloc(int index, IoType ioType, RegisterSize registerSize) + { + _ilBlock.Add(new IlOpCodeLoad(index, ioType, registerSize)); + } + + private void Stloc(int index, IoType ioType) + { + _ilBlock.Add(new IlOpCodeStore(index, ioType, CurrOp.RegisterSize)); + } + + public void EmitCallPropGet(Type objType, string propName) + { + if (objType == null) + { + throw new ArgumentNullException(nameof(objType)); + } + + if (propName == null) + { + throw new ArgumentNullException(nameof(propName)); + } + + EmitCall(objType.GetMethod($"get_{propName}")); + } + + public void EmitCallPropSet(Type objType, string propName) + { + if (objType == null) + { + throw new ArgumentNullException(nameof(objType)); + } + + if (propName == null) + { + throw new ArgumentNullException(nameof(propName)); + } + + EmitCall(objType.GetMethod($"set_{propName}")); + } + + public void EmitCall(Type objType, string mthdName) + { + if (objType == null) + { + throw new ArgumentNullException(nameof(objType)); + } + + if (mthdName == null) + { + throw new ArgumentNullException(nameof(mthdName)); + } + + EmitCall(objType.GetMethod(mthdName)); + } + + public void EmitPrivateCall(Type objType, string mthdName) + { + if (objType == null) + { + throw new ArgumentNullException(nameof(objType)); + } + + if (mthdName == null) + { + throw new ArgumentNullException(nameof(mthdName)); + } + + EmitCall(objType.GetMethod(mthdName, BindingFlags.Instance | BindingFlags.NonPublic)); + } + + public void EmitCall(MethodInfo mthdInfo) + { + if (mthdInfo == null) + { + throw new ArgumentNullException(nameof(mthdInfo)); + } + + _ilBlock.Add(new ILOpCodeCall(mthdInfo)); + } + + public void EmitLdc_I(long value) + { + if (CurrOp.RegisterSize == RegisterSize.Int32) + { + EmitLdc_I4((int)value); + } + else + { + EmitLdc_I8(value); + } + } + + public void EmitLdc_I4(int value) + { + _ilBlock.Add(new ILOpCodeConst(value)); + } + + public void EmitLdc_I8(long value) + { + _ilBlock.Add(new ILOpCodeConst(value)); + } + + public void EmitLdc_R4(float value) + { + _ilBlock.Add(new ILOpCodeConst(value)); + } + + public void EmitLdc_R8(double value) + { + _ilBlock.Add(new ILOpCodeConst(value)); + } + + public void EmitZnFlagCheck() + { + EmitZnCheck(OpCodes.Ceq, (int)PState.ZBit); + EmitZnCheck(OpCodes.Clt, (int)PState.NBit); + } + + private void EmitZnCheck(System.Reflection.Emit.OpCode ilCmpOp, int flag) + { + Emit(OpCodes.Dup); + Emit(OpCodes.Ldc_I4_0); + + if (CurrOp.RegisterSize != RegisterSize.Int32) + { + Emit(OpCodes.Conv_I8); + } + + Emit(ilCmpOp); + + EmitStflg(flag); + } + } +} diff --git a/ChocolArm64/Translation/ILGeneratorEx.cs b/ChocolArm64/Translation/ILGeneratorEx.cs index 40c6efa4d7..318098cc78 100644 --- a/ChocolArm64/Translation/ILGeneratorEx.cs +++ b/ChocolArm64/Translation/ILGeneratorEx.cs @@ -6,123 +6,123 @@ namespace ChocolArm64 static class ILGeneratorEx { - public static void EmitLdc_I4(this ILGenerator Generator, int Value) + public static void EmitLdc_I4(this ILGenerator generator, int value) { - switch (Value) + switch (value) { - case 0: Generator.Emit(OpCodes.Ldc_I4_0); break; - case 1: Generator.Emit(OpCodes.Ldc_I4_1); break; - case 2: Generator.Emit(OpCodes.Ldc_I4_2); break; - case 3: Generator.Emit(OpCodes.Ldc_I4_3); break; - case 4: Generator.Emit(OpCodes.Ldc_I4_4); break; - case 5: Generator.Emit(OpCodes.Ldc_I4_5); break; - case 6: Generator.Emit(OpCodes.Ldc_I4_6); break; - case 7: Generator.Emit(OpCodes.Ldc_I4_7); break; - case 8: Generator.Emit(OpCodes.Ldc_I4_8); break; - case -1: Generator.Emit(OpCodes.Ldc_I4_M1); break; - default: Generator.Emit(OpCodes.Ldc_I4, Value); break; + case 0: generator.Emit(OpCodes.Ldc_I4_0); break; + case 1: generator.Emit(OpCodes.Ldc_I4_1); break; + case 2: generator.Emit(OpCodes.Ldc_I4_2); break; + case 3: generator.Emit(OpCodes.Ldc_I4_3); break; + case 4: generator.Emit(OpCodes.Ldc_I4_4); break; + case 5: generator.Emit(OpCodes.Ldc_I4_5); break; + case 6: generator.Emit(OpCodes.Ldc_I4_6); break; + case 7: generator.Emit(OpCodes.Ldc_I4_7); break; + case 8: generator.Emit(OpCodes.Ldc_I4_8); break; + case -1: generator.Emit(OpCodes.Ldc_I4_M1); break; + default: generator.Emit(OpCodes.Ldc_I4, value); break; } } - public static void EmitLdarg(this ILGenerator Generator, int Index) + public static void EmitLdarg(this ILGenerator generator, int index) { - switch (Index) + switch (index) { - case 0: Generator.Emit(OpCodes.Ldarg_0); break; - case 1: Generator.Emit(OpCodes.Ldarg_1); break; - case 2: Generator.Emit(OpCodes.Ldarg_2); break; - case 3: Generator.Emit(OpCodes.Ldarg_3); break; + case 0: generator.Emit(OpCodes.Ldarg_0); break; + case 1: generator.Emit(OpCodes.Ldarg_1); break; + case 2: generator.Emit(OpCodes.Ldarg_2); break; + case 3: generator.Emit(OpCodes.Ldarg_3); break; default: - if ((uint)Index <= byte.MaxValue) + if ((uint)index <= byte.MaxValue) { - Generator.Emit(OpCodes.Ldarg_S, (byte)Index); + generator.Emit(OpCodes.Ldarg_S, (byte)index); } - else if ((uint)Index < ushort.MaxValue) + else if ((uint)index < ushort.MaxValue) { - Generator.Emit(OpCodes.Ldarg, (short)Index); + generator.Emit(OpCodes.Ldarg, (short)index); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } break; } } - public static void EmitStarg(this ILGenerator Generator, int Index) + public static void EmitStarg(this ILGenerator generator, int index) { - if ((uint)Index <= byte.MaxValue) + if ((uint)index <= byte.MaxValue) { - Generator.Emit(OpCodes.Starg_S, (byte)Index); + generator.Emit(OpCodes.Starg_S, (byte)index); } - else if ((uint)Index < ushort.MaxValue) + else if ((uint)index < ushort.MaxValue) { - Generator.Emit(OpCodes.Starg, (short)Index); + generator.Emit(OpCodes.Starg, (short)index); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } } - public static void EmitLdloc(this ILGenerator Generator, int Index) + public static void EmitLdloc(this ILGenerator generator, int index) { - switch (Index) + switch (index) { - case 0: Generator.Emit(OpCodes.Ldloc_0); break; - case 1: Generator.Emit(OpCodes.Ldloc_1); break; - case 2: Generator.Emit(OpCodes.Ldloc_2); break; - case 3: Generator.Emit(OpCodes.Ldloc_3); break; + case 0: generator.Emit(OpCodes.Ldloc_0); break; + case 1: generator.Emit(OpCodes.Ldloc_1); break; + case 2: generator.Emit(OpCodes.Ldloc_2); break; + case 3: generator.Emit(OpCodes.Ldloc_3); break; default: - if ((uint)Index <= byte.MaxValue) + if ((uint)index <= byte.MaxValue) { - Generator.Emit(OpCodes.Ldloc_S, (byte)Index); + generator.Emit(OpCodes.Ldloc_S, (byte)index); } - else if ((uint)Index < ushort.MaxValue) + else if ((uint)index < ushort.MaxValue) { - Generator.Emit(OpCodes.Ldloc, (short)Index); + generator.Emit(OpCodes.Ldloc, (short)index); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } break; } } - public static void EmitStloc(this ILGenerator Generator, int Index) + public static void EmitStloc(this ILGenerator generator, int index) { - switch (Index) + switch (index) { - case 0: Generator.Emit(OpCodes.Stloc_0); break; - case 1: Generator.Emit(OpCodes.Stloc_1); break; - case 2: Generator.Emit(OpCodes.Stloc_2); break; - case 3: Generator.Emit(OpCodes.Stloc_3); break; + case 0: generator.Emit(OpCodes.Stloc_0); break; + case 1: generator.Emit(OpCodes.Stloc_1); break; + case 2: generator.Emit(OpCodes.Stloc_2); break; + case 3: generator.Emit(OpCodes.Stloc_3); break; default: - if ((uint)Index <= byte.MaxValue) + if ((uint)index <= byte.MaxValue) { - Generator.Emit(OpCodes.Stloc_S, (byte)Index); + generator.Emit(OpCodes.Stloc_S, (byte)index); } - else if ((uint)Index < ushort.MaxValue) + else if ((uint)index < ushort.MaxValue) { - Generator.Emit(OpCodes.Stloc, (short)Index); + generator.Emit(OpCodes.Stloc, (short)index); } else { - throw new ArgumentOutOfRangeException(nameof(Index)); + throw new ArgumentOutOfRangeException(nameof(index)); } break; } } - public static void EmitLdargSeq(this ILGenerator Generator, int Count) + public static void EmitLdargSeq(this ILGenerator generator, int count) { - for (int Index = 0; Index < Count; Index++) + for (int index = 0; index < count; index++) { - Generator.EmitLdarg(Index); + generator.EmitLdarg(index); } } } diff --git a/ChocolArm64/Translation/ILLabel.cs b/ChocolArm64/Translation/ILLabel.cs new file mode 100644 index 0000000000..4f96edccf1 --- /dev/null +++ b/ChocolArm64/Translation/ILLabel.cs @@ -0,0 +1,28 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + class ILLabel : IILEmit + { + private bool _hasLabel; + + private Label _lbl; + + public void Emit(ILEmitter context) + { + context.Generator.MarkLabel(GetLabel(context)); + } + + public Label GetLabel(ILEmitter context) + { + if (!_hasLabel) + { + _lbl = context.Generator.DefineLabel(); + + _hasLabel = true; + } + + return _lbl; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCode.cs b/ChocolArm64/Translation/ILOpCode.cs new file mode 100644 index 0000000000..eb91639e63 --- /dev/null +++ b/ChocolArm64/Translation/ILOpCode.cs @@ -0,0 +1,19 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct ILOpCode : IILEmit + { + private OpCode _ilOp; + + public ILOpCode(OpCode ilOp) + { + _ilOp = ilOp; + } + + public void Emit(ILEmitter context) + { + context.Generator.Emit(_ilOp); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeBranch.cs b/ChocolArm64/Translation/ILOpCodeBranch.cs new file mode 100644 index 0000000000..b0ba23313f --- /dev/null +++ b/ChocolArm64/Translation/ILOpCodeBranch.cs @@ -0,0 +1,21 @@ +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct ILOpCodeBranch : IILEmit + { + private OpCode _ilOp; + private ILLabel _label; + + public ILOpCodeBranch(OpCode ilOp, ILLabel label) + { + _ilOp = ilOp; + _label = label; + } + + public void Emit(ILEmitter context) + { + context.Generator.Emit(_ilOp, _label.GetLabel(context)); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeCall.cs b/ChocolArm64/Translation/ILOpCodeCall.cs new file mode 100644 index 0000000000..0b591d46ce --- /dev/null +++ b/ChocolArm64/Translation/ILOpCodeCall.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct ILOpCodeCall : IILEmit + { + private MethodInfo _mthdInfo; + + public ILOpCodeCall(MethodInfo mthdInfo) + { + _mthdInfo = mthdInfo; + } + + public void Emit(ILEmitter context) + { + context.Generator.Emit(OpCodes.Call, _mthdInfo); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeConst.cs b/ChocolArm64/Translation/ILOpCodeConst.cs new file mode 100644 index 0000000000..5497eba16f --- /dev/null +++ b/ChocolArm64/Translation/ILOpCodeConst.cs @@ -0,0 +1,65 @@ +using System.Reflection.Emit; +using System.Runtime.InteropServices; + +namespace ChocolArm64.Translation +{ + class ILOpCodeConst : IILEmit + { + [StructLayout(LayoutKind.Explicit, Size = 8)] + private struct ImmVal + { + [FieldOffset(0)] public int I4; + [FieldOffset(0)] public long I8; + [FieldOffset(0)] public float R4; + [FieldOffset(0)] public double R8; + } + + private ImmVal _value; + + private enum ConstType + { + Int32, + Int64, + Single, + Double + } + + private ConstType _type; + + private ILOpCodeConst(ConstType type) + { + _type = type; + } + + public ILOpCodeConst(int value) : this(ConstType.Int32) + { + _value = new ImmVal { I4 = value }; + } + + public ILOpCodeConst(long value) : this(ConstType.Int64) + { + _value = new ImmVal { I8 = value }; + } + + public ILOpCodeConst(float value) : this(ConstType.Single) + { + _value = new ImmVal { R4 = value }; + } + + public ILOpCodeConst(double value) : this(ConstType.Double) + { + _value = new ImmVal { R8 = value }; + } + + public void Emit(ILEmitter context) + { + switch (_type) + { + case ConstType.Int32: context.Generator.EmitLdc_I4(_value.I4); break; + case ConstType.Int64: context.Generator.Emit(OpCodes.Ldc_I8, _value.I8); break; + case ConstType.Single: context.Generator.Emit(OpCodes.Ldc_R4, _value.R4); break; + case ConstType.Double: context.Generator.Emit(OpCodes.Ldc_R8, _value.R8); break; + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeLoad.cs b/ChocolArm64/Translation/ILOpCodeLoad.cs new file mode 100644 index 0000000000..9dae10cc4b --- /dev/null +++ b/ChocolArm64/Translation/ILOpCodeLoad.cs @@ -0,0 +1,75 @@ +using ChocolArm64.State; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct IlOpCodeLoad : IILEmit + { + public int Index { get; private set; } + + public IoType IoType { get; private set; } + + public RegisterSize RegisterSize { get; private set; } + + public IlOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0) + { + Index = index; + IoType = ioType; + RegisterSize = registerSize; + } + + public void Emit(ILEmitter context) + { + switch (IoType) + { + case IoType.Arg: context.Generator.EmitLdarg(Index); break; + + case IoType.Fields: + { + long intInputs = context.LocalAlloc.GetIntInputs(context.GetIlBlock(Index)); + long vecInputs = context.LocalAlloc.GetVecInputs(context.GetIlBlock(Index)); + + LoadLocals(context, intInputs, RegisterType.Int); + LoadLocals(context, vecInputs, RegisterType.Vector); + + break; + } + + case IoType.Flag: EmitLdloc(context, Index, RegisterType.Flag); break; + case IoType.Int: EmitLdloc(context, Index, RegisterType.Int); break; + case IoType.Vector: EmitLdloc(context, Index, RegisterType.Vector); break; + } + } + + private void LoadLocals(ILEmitter context, long inputs, RegisterType baseType) + { + for (int bit = 0; bit < 64; bit++) + { + long mask = 1L << bit; + + if ((inputs & mask) != 0) + { + Register reg = ILEmitter.GetRegFromBit(bit, baseType); + + context.Generator.EmitLdarg(TranslatedSub.StateArgIdx); + context.Generator.Emit(OpCodes.Ldfld, reg.GetField()); + + context.Generator.EmitStloc(context.GetLocalIndex(reg)); + } + } + } + + private void EmitLdloc(ILEmitter context, int index, RegisterType registerType) + { + Register reg = new Register(index, registerType); + + context.Generator.EmitLdloc(context.GetLocalIndex(reg)); + + if (registerType == RegisterType.Int && + RegisterSize == RegisterSize.Int32) + { + context.Generator.Emit(OpCodes.Conv_U4); + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeLog.cs b/ChocolArm64/Translation/ILOpCodeLog.cs new file mode 100644 index 0000000000..2c77021b10 --- /dev/null +++ b/ChocolArm64/Translation/ILOpCodeLog.cs @@ -0,0 +1,17 @@ +namespace ChocolArm64.Translation +{ + struct ILOpCodeLog : IILEmit + { + private string _text; + + public ILOpCodeLog(string text) + { + _text = text; + } + + public void Emit(ILEmitter context) + { + context.Generator.EmitWriteLine(_text); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeStore.cs b/ChocolArm64/Translation/ILOpCodeStore.cs new file mode 100644 index 0000000000..41326fe127 --- /dev/null +++ b/ChocolArm64/Translation/ILOpCodeStore.cs @@ -0,0 +1,75 @@ +using ChocolArm64.State; +using System.Reflection.Emit; + +namespace ChocolArm64.Translation +{ + struct IlOpCodeStore : IILEmit + { + public int Index { get; private set; } + + public IoType IoType { get; private set; } + + public RegisterSize RegisterSize { get; private set; } + + public IlOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0) + { + Index = index; + IoType = ioType; + RegisterSize = registerSize; + } + + public void Emit(ILEmitter context) + { + switch (IoType) + { + case IoType.Arg: context.Generator.EmitStarg(Index); break; + + case IoType.Fields: + { + long intOutputs = context.LocalAlloc.GetIntOutputs(context.GetIlBlock(Index)); + long vecOutputs = context.LocalAlloc.GetVecOutputs(context.GetIlBlock(Index)); + + StoreLocals(context, intOutputs, RegisterType.Int); + StoreLocals(context, vecOutputs, RegisterType.Vector); + + break; + } + + case IoType.Flag: EmitStloc(context, Index, RegisterType.Flag); break; + case IoType.Int: EmitStloc(context, Index, RegisterType.Int); break; + case IoType.Vector: EmitStloc(context, Index, RegisterType.Vector); break; + } + } + + private void StoreLocals(ILEmitter context, long outputs, RegisterType baseType) + { + for (int bit = 0; bit < 64; bit++) + { + long mask = 1L << bit; + + if ((outputs & mask) != 0) + { + Register reg = ILEmitter.GetRegFromBit(bit, baseType); + + context.Generator.EmitLdarg(TranslatedSub.StateArgIdx); + context.Generator.EmitLdloc(context.GetLocalIndex(reg)); + + context.Generator.Emit(OpCodes.Stfld, reg.GetField()); + } + } + } + + private void EmitStloc(ILEmitter context, int index, RegisterType registerType) + { + Register reg = new Register(index, registerType); + + if (registerType == RegisterType.Int && + RegisterSize == RegisterSize.Int32) + { + context.Generator.Emit(OpCodes.Conv_U8); + } + + context.Generator.EmitStloc(context.GetLocalIndex(reg)); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/AIoType.cs b/ChocolArm64/Translation/IoType.cs similarity index 90% rename from ChocolArm64/Translation/AIoType.cs rename to ChocolArm64/Translation/IoType.cs index 94f8908142..290b159fc5 100644 --- a/ChocolArm64/Translation/AIoType.cs +++ b/ChocolArm64/Translation/IoType.cs @@ -3,7 +3,7 @@ using System; namespace ChocolArm64.Translation { [Flags] - enum AIoType + enum IoType { Arg, Fields, diff --git a/ChocolArm64/Translation/LocalAlloc.cs b/ChocolArm64/Translation/LocalAlloc.cs new file mode 100644 index 0000000000..8237bd5454 --- /dev/null +++ b/ChocolArm64/Translation/LocalAlloc.cs @@ -0,0 +1,229 @@ +using System.Collections.Generic; + +namespace ChocolArm64.Translation +{ + class LocalAlloc + { + private class PathIo + { + private Dictionary _allInputs; + private Dictionary _cmnOutputs; + + private long _allOutputs; + + public PathIo() + { + _allInputs = new Dictionary(); + _cmnOutputs = new Dictionary(); + } + + public PathIo(ILBlock root, long inputs, long outputs) : this() + { + Set(root, inputs, outputs); + } + + public void Set(ILBlock root, long inputs, long outputs) + { + if (!_allInputs.TryAdd(root, inputs)) + { + _allInputs[root] |= inputs; + } + + if (!_cmnOutputs.TryAdd(root, outputs)) + { + _cmnOutputs[root] &= outputs; + } + + _allOutputs |= outputs; + } + + public long GetInputs(ILBlock root) + { + if (_allInputs.TryGetValue(root, out long inputs)) + { + return inputs | (_allOutputs & ~_cmnOutputs[root]); + } + + return 0; + } + + public long GetOutputs() + { + return _allOutputs; + } + } + + private Dictionary _intPaths; + private Dictionary _vecPaths; + + private struct BlockIo + { + public ILBlock Block; + public ILBlock Entry; + + public long IntInputs; + public long VecInputs; + public long IntOutputs; + public long VecOutputs; + } + + private const int MaxOptGraphLength = 40; + + public LocalAlloc(ILBlock[] graph, ILBlock root) + { + _intPaths = new Dictionary(); + _vecPaths = new Dictionary(); + + if (graph.Length > 1 && + graph.Length < MaxOptGraphLength) + { + InitializeOptimal(graph, root); + } + else + { + InitializeFast(graph); + } + } + + private void InitializeOptimal(ILBlock[] graph, ILBlock root) + { + //This will go through all possible paths on the graph, + //and store all inputs/outputs for each block. A register + //that was previously written to already is not considered an input. + //When a block can be reached by more than one path, then the + //output from all paths needs to be set for this block, and + //only outputs present in all of the parent blocks can be considered + //when doing input elimination. Each block chain have a root, that's where + //the code starts executing. They are present on the subroutine start point, + //and on call return points too (address written to X30 by BL). + HashSet visited = new HashSet(); + + Queue unvisited = new Queue(); + + void Enqueue(BlockIo block) + { + if (!visited.Contains(block)) + { + unvisited.Enqueue(block); + + visited.Add(block); + } + } + + Enqueue(new BlockIo() + { + Block = root, + Entry = root + }); + + while (unvisited.Count > 0) + { + BlockIo current = unvisited.Dequeue(); + + current.IntInputs |= current.Block.IntInputs & ~current.IntOutputs; + current.VecInputs |= current.Block.VecInputs & ~current.VecOutputs; + current.IntOutputs |= current.Block.IntOutputs; + current.VecOutputs |= current.Block.VecOutputs; + + //Check if this is a exit block + //(a block that returns or calls another sub). + if ((current.Block.Next == null && + current.Block.Branch == null) || current.Block.HasStateStore) + { + if (!_intPaths.TryGetValue(current.Block, out PathIo intPath)) + { + _intPaths.Add(current.Block, intPath = new PathIo()); + } + + if (!_vecPaths.TryGetValue(current.Block, out PathIo vecPath)) + { + _vecPaths.Add(current.Block, vecPath = new PathIo()); + } + + intPath.Set(current.Entry, current.IntInputs, current.IntOutputs); + vecPath.Set(current.Entry, current.VecInputs, current.VecOutputs); + } + + void EnqueueFromCurrent(ILBlock block, bool retTarget) + { + BlockIo blkIO = new BlockIo() { Block = block }; + + if (retTarget) + { + blkIO.Entry = block; + } + else + { + blkIO.Entry = current.Entry; + blkIO.IntInputs = current.IntInputs; + blkIO.VecInputs = current.VecInputs; + blkIO.IntOutputs = current.IntOutputs; + blkIO.VecOutputs = current.VecOutputs; + } + + Enqueue(blkIO); + } + + if (current.Block.Next != null) + { + EnqueueFromCurrent(current.Block.Next, current.Block.HasStateStore); + } + + if (current.Block.Branch != null) + { + EnqueueFromCurrent(current.Block.Branch, false); + } + } + } + + private void InitializeFast(ILBlock[] graph) + { + //This is WAY faster than InitializeOptimal, but results in + //uneeded loads and stores, so the resulting code will be slower. + long intInputs = 0, intOutputs = 0; + long vecInputs = 0, vecOutputs = 0; + + foreach (ILBlock block in graph) + { + intInputs |= block.IntInputs; + intOutputs |= block.IntOutputs; + vecInputs |= block.VecInputs; + vecOutputs |= block.VecOutputs; + } + + //It's possible that not all code paths writes to those output registers, + //in those cases if we attempt to write an output registers that was + //not written, we will be just writing zero and messing up the old register value. + //So we just need to ensure that all outputs are loaded. + if (graph.Length > 1) + { + intInputs |= intOutputs; + vecInputs |= vecOutputs; + } + + foreach (ILBlock block in graph) + { + _intPaths.Add(block, new PathIo(block, intInputs, intOutputs)); + _vecPaths.Add(block, new PathIo(block, vecInputs, vecOutputs)); + } + } + + public long GetIntInputs(ILBlock root) => GetInputsImpl(root, _intPaths.Values); + public long GetVecInputs(ILBlock root) => GetInputsImpl(root, _vecPaths.Values); + + private long GetInputsImpl(ILBlock root, IEnumerable values) + { + long inputs = 0; + + foreach (PathIo path in values) + { + inputs |= path.GetInputs(root); + } + + return inputs; + } + + public long GetIntOutputs(ILBlock block) => _intPaths[block].GetOutputs(); + public long GetVecOutputs(ILBlock block) => _vecPaths[block].GetOutputs(); + } +} \ No newline at end of file diff --git a/ChocolArm64/Translator.cs b/ChocolArm64/Translator.cs new file mode 100644 index 0000000000..3bf06dc469 --- /dev/null +++ b/ChocolArm64/Translator.cs @@ -0,0 +1,165 @@ +using ChocolArm64.Decoders; +using ChocolArm64.Events; +using ChocolArm64.Memory; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +namespace ChocolArm64 +{ + public class Translator + { + private TranslatorCache _cache; + + public event EventHandler CpuTrace; + + public bool EnableCpuTrace { get; set; } + + public Translator() + { + _cache = new TranslatorCache(); + } + + internal void ExecuteSubroutine(CpuThread thread, long position) + { + //TODO: Both the execute A32/A64 methods should be merged on the future, + //when both ISAs are implemented with the interpreter and JIT. + //As of now, A32 only has a interpreter and A64 a JIT. + CpuThreadState state = thread.ThreadState; + MemoryManager memory = thread.Memory; + + if (state.ExecutionMode == ExecutionMode.AArch32) + { + ExecuteSubroutineA32(state, memory); + } + else + { + ExecuteSubroutineA64(state, memory, position); + } + } + + private void ExecuteSubroutineA32(CpuThreadState state, MemoryManager memory) + { + do + { + OpCode64 opCode = Decoder.DecodeOpCode(state, memory, state.R15); + + opCode.Interpreter(state, memory, opCode); + } + while (state.R15 != 0 && state.Running); + } + + private void ExecuteSubroutineA64(CpuThreadState state, MemoryManager memory, long position) + { + do + { + if (EnableCpuTrace) + { + CpuTrace?.Invoke(this, new CpuTraceEventArgs(position)); + } + + if (!_cache.TryGetSubroutine(position, out TranslatedSub sub)) + { + sub = TranslateTier0(state, memory, position); + } + + if (sub.ShouldReJit()) + { + TranslateTier1(state, memory, position); + } + + position = sub.Execute(state, memory); + } + while (position != 0 && state.Running); + } + + internal bool HasCachedSub(long position) + { + return _cache.HasSubroutine(position); + } + + private TranslatedSub TranslateTier0(CpuThreadState state, MemoryManager memory, long position) + { + Block block = Decoder.DecodeBasicBlock(state, memory, position); + + Block[] graph = new Block[] { block }; + + string subName = GetSubroutineName(position); + + ILEmitterCtx context = new ILEmitterCtx(_cache, graph, block, subName); + + do + { + context.EmitOpCode(); + } + while (context.AdvanceOpCode()); + + TranslatedSub subroutine = context.GetSubroutine(); + + subroutine.SetType(TranslatedSubType.SubTier0); + + _cache.AddOrUpdate(position, subroutine, block.OpCodes.Count); + + OpCode64 lastOp = block.GetLastOp(); + + return subroutine; + } + + private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position) + { + (Block[] graph, Block root) = Decoder.DecodeSubroutine(_cache, state, memory, position); + + string subName = GetSubroutineName(position); + + ILEmitterCtx context = new ILEmitterCtx(_cache, graph, root, subName); + + if (context.CurrBlock.Position != position) + { + context.Emit(OpCodes.Br, context.GetLabel(position)); + } + + do + { + context.EmitOpCode(); + } + while (context.AdvanceOpCode()); + + //Mark all methods that calls this method for ReJiting, + //since we can now call it directly which is faster. + if (_cache.TryGetSubroutine(position, out TranslatedSub oldSub)) + { + foreach (long callerPos in oldSub.GetCallerPositions()) + { + if (_cache.TryGetSubroutine(position, out TranslatedSub callerSub)) + { + callerSub.MarkForReJit(); + } + } + } + + TranslatedSub subroutine = context.GetSubroutine(); + + subroutine.SetType(TranslatedSubType.SubTier1); + + _cache.AddOrUpdate(position, subroutine, GetGraphInstCount(graph)); + } + + private string GetSubroutineName(long position) + { + return $"Sub{position:x16}"; + } + + private int GetGraphInstCount(Block[] graph) + { + int size = 0; + + foreach (Block block in graph) + { + size += block.OpCodes.Count; + } + + return size; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/TranslatorCache.cs b/ChocolArm64/TranslatorCache.cs new file mode 100644 index 0000000000..7d65035777 --- /dev/null +++ b/ChocolArm64/TranslatorCache.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace ChocolArm64 +{ + class TranslatorCache + { + //Maximum size of the cache, in bytes, measured in ARM code size. + private const int MaxTotalSize = 4 * 1024 * 256; + + //Minimum time required in milliseconds for a method to be eligible for deletion. + private const int MinTimeDelta = 2 * 60000; + + //Minimum number of calls required to update the timestamp. + private const int MinCallCountForUpdate = 250; + + private class CacheBucket + { + public TranslatedSub Subroutine { get; private set; } + + public LinkedListNode Node { get; private set; } + + public int CallCount { get; set; } + + public int Size { get; private set; } + + public long Timestamp { get; private set; } + + public CacheBucket(TranslatedSub subroutine, LinkedListNode node, int size) + { + Subroutine = subroutine; + Size = size; + + UpdateNode(node); + } + + public void UpdateNode(LinkedListNode node) + { + Node = node; + + Timestamp = GetTimestamp(); + } + } + + private ConcurrentDictionary _cache; + + private LinkedList _sortedCache; + + private int _totalSize; + + public TranslatorCache() + { + _cache = new ConcurrentDictionary(); + + _sortedCache = new LinkedList(); + } + + public void AddOrUpdate(long position, TranslatedSub subroutine, int size) + { + ClearCacheIfNeeded(); + + _totalSize += size; + + lock (_sortedCache) + { + LinkedListNode node = _sortedCache.AddLast(position); + + CacheBucket newBucket = new CacheBucket(subroutine, node, size); + + _cache.AddOrUpdate(position, newBucket, (key, bucket) => + { + _totalSize -= bucket.Size; + + _sortedCache.Remove(bucket.Node); + + return newBucket; + }); + } + } + + public bool HasSubroutine(long position) + { + return _cache.ContainsKey(position); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetSubroutine(long position, out TranslatedSub subroutine) + { + if (_cache.TryGetValue(position, out CacheBucket bucket)) + { + if (bucket.CallCount++ > MinCallCountForUpdate) + { + if (Monitor.TryEnter(_sortedCache)) + { + try + { + bucket.CallCount = 0; + + _sortedCache.Remove(bucket.Node); + + bucket.UpdateNode(_sortedCache.AddLast(position)); + } + finally + { + Monitor.Exit(_sortedCache); + } + } + } + + subroutine = bucket.Subroutine; + + return true; + } + + subroutine = default(TranslatedSub); + + return false; + } + + private void ClearCacheIfNeeded() + { + long timestamp = GetTimestamp(); + + while (_totalSize > MaxTotalSize) + { + lock (_sortedCache) + { + LinkedListNode node = _sortedCache.First; + + if (node == null) + { + break; + } + + CacheBucket bucket = _cache[node.Value]; + + long timeDelta = timestamp - bucket.Timestamp; + + if (timeDelta <= MinTimeDelta) + { + break; + } + + if (_cache.TryRemove(node.Value, out bucket)) + { + _totalSize -= bucket.Size; + + _sortedCache.Remove(bucket.Node); + } + } + } + } + + private static long GetTimestamp() + { + long timestamp = Stopwatch.GetTimestamp(); + + return timestamp / (Stopwatch.Frequency / 1000); + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index f6bac98c8c..bf195287b6 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,6 @@ https://openal.org/downloads/OpenAL11CoreSDK.zip - Config File: `Ryujinx.conf` should be present in executable folder. For more information [you can go here](CONFIG.md). - - If you are a Windows user, you can configure your keys, the logs, install OpenAL, etc... with Ryujinx-Setting. - [Download it, right here](https://github.com/AcK77/Ryujinx-Settings) - **Help** If you have some homebrew that currently doesn't work within the emulator, you can contact us through our Discord with the compiled NRO/NSO (and source code if possible) and then we'll make changes in order to make the requested app / game work. diff --git a/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs b/Ryujinx.Audio/Decoders/Adpcm/AdpcmDecoder.cs similarity index 100% rename from Ryujinx.Audio/Adpcm/AdpcmDecoder.cs rename to Ryujinx.Audio/Decoders/Adpcm/AdpcmDecoder.cs diff --git a/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs b/Ryujinx.Audio/Decoders/Adpcm/AdpcmDecoderContext.cs similarity index 100% rename from Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs rename to Ryujinx.Audio/Decoders/Adpcm/AdpcmDecoderContext.cs diff --git a/Ryujinx.Audio/IAalOutput.cs b/Ryujinx.Audio/IAalOutput.cs index 1dfac377f7..119fc2342d 100644 --- a/Ryujinx.Audio/IAalOutput.cs +++ b/Ryujinx.Audio/IAalOutput.cs @@ -4,19 +4,19 @@ namespace Ryujinx.Audio { public interface IAalOutput : IDisposable { - int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback); + int OpenTrack(int sampleRate, int channels, ReleaseCallback callback); - void CloseTrack(int Track); + void CloseTrack(int trackId); - bool ContainsBuffer(int Track, long Tag); + bool ContainsBuffer(int trackId, long bufferTag); - long[] GetReleasedBuffers(int Track, int MaxCount); + long[] GetReleasedBuffers(int trackId, int maxCount); - void AppendBuffer(int Track, long Tag, T[] Buffer) where T : struct; + void AppendBuffer(int trackId, long bufferTag, T[] buffer) where T : struct; - void Start(int Track); - void Stop(int Track); + void Start(int trackId); + void Stop(int trackId); - PlaybackState GetState(int Track); + PlaybackState GetState(int trackId); } } \ No newline at end of file diff --git a/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs b/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs new file mode 100644 index 0000000000..ec3eef3757 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/MarshalExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public static class MarshalEx + { + public static double ReadDouble (IntPtr handle, int offset = 0) + { + return BitConverter.Int64BitsToDouble (Marshal.ReadInt64 (handle, offset)); + } + + public static void WriteDouble (IntPtr handle, double value) + { + WriteDouble (handle, 0, value); + } + + public static void WriteDouble (IntPtr handle, int offset, double value) + { + Marshal.WriteInt64 (handle, offset, BitConverter.DoubleToInt64Bits (value)); + } + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIO.cs b/Ryujinx.Audio/Native/libsoundio/SoundIO.cs new file mode 100644 index 0000000000..e9ab9e6e1a --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIO.cs @@ -0,0 +1,311 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public class SoundIO : IDisposable + { + Pointer handle; + + public SoundIO () + { + handle = Natives.soundio_create (); + } + + internal SoundIO (Pointer handle) + { + this.handle = handle; + } + + public void Dispose () + { + foreach (var h in allocated_hglobals) + Marshal.FreeHGlobal (h); + Natives.soundio_destroy (handle); + } + + // Equality (based on handle) + + public override bool Equals (object other) + { + var d = other as SoundIO; + return d != null && this.handle == d.handle; + } + + public override int GetHashCode () + { + return (int) (IntPtr) handle; + } + + public static bool operator == (SoundIO obj1, SoundIO obj2) + { + return (object)obj1 == null ? (object)obj2 == null : obj1.Equals (obj2); + } + + public static bool operator != (SoundIO obj1, SoundIO obj2) + { + return (object)obj1 == null ? (object)obj2 != null : !obj1.Equals (obj2); + } + + // fields + + // FIXME: this should be taken care in more centralized/decent manner... we don't want to write + // this kind of code anywhere we need string marshaling. + List allocated_hglobals = new List (); + + public string ApplicationName { + get { return Marshal.PtrToStringAnsi (Marshal.ReadIntPtr (handle, app_name_offset)); } + set { + unsafe { + var existing = Marshal.ReadIntPtr (handle, app_name_offset); + if (allocated_hglobals.Contains (existing)) { + allocated_hglobals.Remove (existing); + Marshal.FreeHGlobal (existing); + } + var ptr = Marshal.StringToHGlobalAnsi (value); + Marshal.WriteIntPtr (handle, app_name_offset, ptr); + allocated_hglobals.Add (ptr); + } + } + } + static readonly int app_name_offset = (int)Marshal.OffsetOf ("app_name"); + + public SoundIOBackend CurrentBackend { + get { return (SoundIOBackend) Marshal.ReadInt32 (handle, current_backend_offset); } + } + static readonly int current_backend_offset = (int)Marshal.OffsetOf ("current_backend"); + + // emit_rtprio_warning + public Action EmitRealtimePriorityWarning { + get { return emit_rtprio_warning; } + set { + emit_rtprio_warning = value; + var ptr = Marshal.GetFunctionPointerForDelegate (on_devices_change); + Marshal.WriteIntPtr (handle, emit_rtprio_warning_offset, ptr); + } + } + static readonly int emit_rtprio_warning_offset = (int)Marshal.OffsetOf ("emit_rtprio_warning"); + Action emit_rtprio_warning; + + // jack_error_callback + public Action JackErrorCallback { + get { return jack_error_callback; } + set { + jack_error_callback = value; + if (value == null) + jack_error_callback = null; + else + jack_error_callback_native = msg => jack_error_callback (msg); + var ptr = Marshal.GetFunctionPointerForDelegate (jack_error_callback_native); + Marshal.WriteIntPtr (handle, jack_error_callback_offset, ptr); + } + } + static readonly int jack_error_callback_offset = (int)Marshal.OffsetOf ("jack_error_callback"); + Action jack_error_callback; + delegate void jack_error_delegate (string message); + jack_error_delegate jack_error_callback_native; + + // jack_info_callback + public Action JackInfoCallback { + get { return jack_info_callback; } + set { + jack_info_callback = value; + if (value == null) + jack_info_callback = null; + else + jack_info_callback_native = msg => jack_info_callback (msg); + var ptr = Marshal.GetFunctionPointerForDelegate (jack_info_callback_native); + Marshal.WriteIntPtr (handle, jack_info_callback_offset, ptr); + } + } + static readonly int jack_info_callback_offset = (int)Marshal.OffsetOf ("jack_info_callback"); + Action jack_info_callback; + delegate void jack_info_delegate (string message); + jack_info_delegate jack_info_callback_native; + + // on_backend_disconnect + public Action OnBackendDisconnect { + get { return on_backend_disconnect; } + set { + on_backend_disconnect = value; + if (value == null) + on_backend_disconnect_native = null; + else + on_backend_disconnect_native = (sio, err) => on_backend_disconnect (err); + var ptr = Marshal.GetFunctionPointerForDelegate (on_backend_disconnect_native); + Marshal.WriteIntPtr (handle, on_backend_disconnect_offset, ptr); + } + } + static readonly int on_backend_disconnect_offset = (int)Marshal.OffsetOf ("on_backend_disconnect"); + Action on_backend_disconnect; + delegate void on_backend_disconnect_delegate (IntPtr handle, int errorCode); + on_backend_disconnect_delegate on_backend_disconnect_native; + + // on_devices_change + public Action OnDevicesChange { + get { return on_devices_change; } + set { + on_devices_change = value; + if (value == null) + on_devices_change_native = null; + else + on_devices_change_native = sio => on_devices_change (); + var ptr = Marshal.GetFunctionPointerForDelegate (on_devices_change_native); + Marshal.WriteIntPtr (handle, on_devices_change_offset, ptr); + } + } + static readonly int on_devices_change_offset = (int)Marshal.OffsetOf ("on_devices_change"); + Action on_devices_change; + delegate void on_devices_change_delegate (IntPtr handle); + on_devices_change_delegate on_devices_change_native; + + // on_events_signal + public Action OnEventsSignal { + get { return on_events_signal; } + set { + on_events_signal = value; + if (value == null) + on_events_signal_native = null; + else + on_events_signal_native = sio => on_events_signal (); + var ptr = Marshal.GetFunctionPointerForDelegate (on_events_signal_native); + Marshal.WriteIntPtr (handle, on_events_signal_offset, ptr); + } + } + static readonly int on_events_signal_offset = (int)Marshal.OffsetOf ("on_events_signal"); + Action on_events_signal; + delegate void on_events_signal_delegate (IntPtr handle); + on_events_signal_delegate on_events_signal_native; + + + // functions + + public int BackendCount { + get { return Natives.soundio_backend_count (handle); } + } + + public int InputDeviceCount { + get { return Natives.soundio_input_device_count (handle); } + } + + public int OutputDeviceCount { + get { return Natives.soundio_output_device_count (handle); } + } + + public int DefaultInputDeviceIndex { + get { return Natives.soundio_default_input_device_index (handle); } + } + + public int DefaultOutputDeviceIndex { + get { return Natives.soundio_default_output_device_index (handle); } + } + + public SoundIOBackend GetBackend (int index) + { + return (SoundIOBackend) Natives.soundio_get_backend (handle, index); + } + + public SoundIODevice GetInputDevice (int index) + { + return new SoundIODevice (Natives.soundio_get_input_device (handle, index)); + } + + public SoundIODevice GetOutputDevice (int index) + { + return new SoundIODevice (Natives.soundio_get_output_device (handle, index)); + } + + public void Connect () + { + var ret = (SoundIoError) Natives.soundio_connect (handle); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public void ConnectBackend (SoundIOBackend backend) + { + var ret = (SoundIoError) Natives.soundio_connect_backend (handle, (SoundIoBackend) backend); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public void Disconnect () + { + Natives.soundio_disconnect (handle); + } + + public void FlushEvents () + { + Natives.soundio_flush_events (handle); + } + + public void WaitEvents () + { + Natives.soundio_wait_events (handle); + } + + public void Wakeup () + { + Natives.soundio_wakeup (handle); + } + + public void ForceDeviceScan () + { + Natives.soundio_force_device_scan (handle); + } + + public SoundIORingBuffer CreateRingBuffer (int capacity) + { + return new SoundIORingBuffer (Natives.soundio_ring_buffer_create (handle, capacity)); + } + + // static methods + + public static string VersionString { + get { return Marshal.PtrToStringAnsi (Natives.soundio_version_string ()); } + } + + public static int VersionMajor { + get { return Natives.soundio_version_major (); } + } + + public static int VersionMinor { + get { return Natives.soundio_version_minor (); } + } + + public static int VersionPatch { + get { return Natives.soundio_version_patch (); } + } + + public static string GetBackendName (SoundIOBackend backend) + { + return Marshal.PtrToStringAnsi (Natives.soundio_backend_name ((SoundIoBackend) backend)); + } + + public static bool HaveBackend (SoundIOBackend backend) + { + return Natives.soundio_have_backend ((SoundIoBackend) backend); + } + + public static int GetBytesPerSample (SoundIOFormat format) + { + return Natives.soundio_get_bytes_per_sample ((SoundIoFormat) format); + } + + public static int GetBytesPerFrame (SoundIOFormat format, int channelCount) + { + return Natives.soundio_get_bytes_per_frame ((SoundIoFormat) format, channelCount); + } + + public static int GetBytesPerSecond (SoundIOFormat format, int channelCount, int sampleRate) + { + return Natives.soundio_get_bytes_per_second ((SoundIoFormat) format, channelCount, sampleRate); + } + + public static string GetSoundFormatName (SoundIOFormat format) + { + return Marshal.PtrToStringAnsi (Natives.soundio_format_string ((SoundIoFormat) format)); + } + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOBackend.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOBackend.cs new file mode 100644 index 0000000000..dfcb0a3f03 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOBackend.cs @@ -0,0 +1,15 @@ +using System; +namespace SoundIOSharp +{ + public enum SoundIOBackend + { + None = 0, + Jack = 1, + PulseAudio = 2, + Alsa = 3, + CoreAudio = 4, + Wasapi = 5, + Dummy = 6, + } + +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelArea.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOChannelArea.cs new file mode 100644 index 0000000000..f30e2bbb44 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOChannelArea.cs @@ -0,0 +1,26 @@ +using System; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public struct SoundIOChannelArea + { + internal SoundIOChannelArea (Pointer handle) + { + this.handle = handle; + } + + Pointer handle; + + public IntPtr Pointer { + get { return Marshal.ReadIntPtr (handle, ptr_offset); } + set { Marshal.WriteIntPtr (handle, ptr_offset, value); } + } + static readonly int ptr_offset = (int) Marshal.OffsetOf ("ptr"); + + public int Step { + get { return Marshal.ReadInt32 (handle, step_offset); } + } + static readonly int step_offset = (int)Marshal.OffsetOf ("step"); + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelAreas.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOChannelAreas.cs new file mode 100644 index 0000000000..776d657acb --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOChannelAreas.cs @@ -0,0 +1,33 @@ +using System; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public struct SoundIOChannelAreas + { + static readonly int native_size = Marshal.SizeOf (); + + internal SoundIOChannelAreas (IntPtr head, int channelCount, int frameCount) + { + this.head = head; + this.channel_count = channelCount; + this.frame_count = frameCount; + } + + IntPtr head; + int channel_count; + int frame_count; + + public bool IsEmpty { + get { return head == IntPtr.Zero; } + } + + public SoundIOChannelArea GetArea (int channel) + { + return new SoundIOChannelArea (head + native_size * channel); + } + + public int ChannelCount => channel_count; + public int FrameCount => frame_count; + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelId.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOChannelId.cs new file mode 100644 index 0000000000..d24508a1f1 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOChannelId.cs @@ -0,0 +1,77 @@ +using System; +namespace SoundIOSharp +{ + + public enum SoundIOChannelId + { + Invalid = 0, + FrontLeft = 1, + FrontRight = 2, + FrontCenter = 3, + Lfe = 4, + BackLeft = 5, + BackRight = 6, + FrontLeftCenter = 7, + FrontRightCenter = 8, + BackCenter = 9, + SideLeft = 10, + SideRight = 11, + TopCenter = 12, + TopFrontLeft = 13, + TopFrontCenter = 14, + TopFrontRight = 15, + TopBackLeft = 16, + TopBackCenter = 17, + TopBackRight = 18, + BackLeftCenter = 19, + BackRightCenter = 20, + FrontLeftWide = 21, + FrontRightWide = 22, + FrontLeftHigh = 23, + FrontCenterHigh = 24, + FrontRightHigh = 25, + TopFrontLeftCenter = 26, + TopFrontRightCenter = 27, + TopSideLeft = 28, + TopSideRight = 29, + LeftLfe = 30, + RightLfe = 31, + Lfe2 = 32, + BottomCenter = 33, + BottomLeftCenter = 34, + BottomRightCenter = 35, + MsMid = 36, + MsSide = 37, + AmbisonicW = 38, + AmbisonicX = 39, + AmbisonicY = 40, + AmbisonicZ = 41, + XyX = 42, + XyY = 43, + HeadphonesLeft = 44, + HeadphonesRight = 45, + ClickTrack = 46, + ForeignLanguage = 47, + HearingImpaired = 48, + Narration = 49, + Haptic = 50, + DialogCentricMix = 51, + Aux = 52, + Aux0 = 53, + Aux1 = 54, + Aux2 = 55, + Aux3 = 56, + Aux4 = 57, + Aux5 = 58, + Aux6 = 59, + Aux7 = 60, + Aux8 = 61, + Aux9 = 62, + Aux10 = 63, + Aux11 = 64, + Aux12 = 65, + Aux13 = 66, + Aux14 = 67, + Aux15 = 68, + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOChannelLayout.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOChannelLayout.cs new file mode 100644 index 0000000000..ee63454200 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOChannelLayout.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public struct SoundIOChannelLayout + { + public static int BuiltInCount { + get { return Natives.soundio_channel_layout_builtin_count (); } + } + + public static SoundIOChannelLayout GetBuiltIn (int index) + { + return new SoundIOChannelLayout (Natives.soundio_channel_layout_get_builtin (index)); + } + + public static SoundIOChannelLayout GetDefault (int channelCount) + { + var handle = Natives.soundio_channel_layout_get_default (channelCount); + return new SoundIOChannelLayout (handle); + } + + public static SoundIOChannelId ParseChannelId (string name) + { + var ptr = Marshal.StringToHGlobalAnsi (name); + try { + return (SoundIOChannelId)Natives.soundio_parse_channel_id (ptr, name.Length); + } finally { + Marshal.FreeHGlobal (ptr); + } + } + + // instance members + + internal SoundIOChannelLayout (Pointer handle) + { + this.handle = handle; + } + + readonly Pointer handle; + + public bool IsNull { + get { return handle.Handle == IntPtr.Zero; } + } + + internal IntPtr Handle { + get { return handle; } + } + + public int ChannelCount { + get { return IsNull ? 0 : Marshal.ReadInt32 ((IntPtr) handle + channel_count_offset); } + } + static readonly int channel_count_offset = (int) Marshal.OffsetOf ("channel_count"); + + public string Name { + get { return IsNull ? null : Marshal.PtrToStringAnsi (Marshal.ReadIntPtr ((IntPtr) handle + name_offset)); } + } + static readonly int name_offset = (int)Marshal.OffsetOf ("name"); + + public IEnumerable Channels { + get { + if (IsNull) + yield break; + for (int i = 0; i < 24; i++) + yield return (SoundIOChannelId) Marshal.ReadInt32 ((IntPtr) handle + channels_offset + sizeof (SoundIoChannelId) * i); + } + } + static readonly int channels_offset = (int)Marshal.OffsetOf ("channels"); + + public override bool Equals (object other) + { + if (!(other is SoundIOChannelLayout)) + return false; + var s = (SoundIOChannelLayout) other; + return handle == s.handle || Natives.soundio_channel_layout_equal (handle, s.handle); + } + + public override int GetHashCode () + { + return handle.GetHashCode (); + } + + public string DetectBuiltInName () + { + if (IsNull) + throw new InvalidOperationException (); + return Natives.soundio_channel_layout_detect_builtin (handle) ? Name : null; + } + + public int FindChannel (SoundIOChannelId channel) + { + if (IsNull) + throw new InvalidOperationException (); + return Natives.soundio_channel_layout_find_channel (handle, (SoundIoChannelId) channel); + } + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIODevice.cs b/Ryujinx.Audio/Native/libsoundio/SoundIODevice.cs new file mode 100644 index 0000000000..81b78b679f --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIODevice.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public class SoundIODevice + { + public static SoundIOChannelLayout BestMatchingChannelLayout (SoundIODevice device1, SoundIODevice device2) + { + var ptr1 = Marshal.ReadIntPtr (device1.handle, layouts_offset); + var ptr2 = Marshal.ReadIntPtr (device2.handle, layouts_offset); + return new SoundIOChannelLayout (Natives.soundio_best_matching_channel_layout (ptr1, device1.LayoutCount, ptr2, device2.LayoutCount)); + } + + internal SoundIODevice (Pointer handle) + { + this.handle = handle; + } + + readonly Pointer handle; + + // Equality (based on handle and native func) + + public override bool Equals (object other) + { + var d = other as SoundIODevice; + return d != null && (this.handle == d.handle || Natives.soundio_device_equal (this.handle, d.handle)); + } + + public override int GetHashCode () + { + return (int) (IntPtr) handle; + } + + public static bool operator == (SoundIODevice obj1, SoundIODevice obj2) + { + return (object)obj1 == null ? (object)obj2 == null : obj1.Equals (obj2); + } + + public static bool operator != (SoundIODevice obj1, SoundIODevice obj2) + { + return (object)obj1 == null ? (object) obj2 != null : !obj1.Equals (obj2); + } + + // fields + + public SoundIODeviceAim Aim { + get { return (SoundIODeviceAim) Marshal.ReadInt32 (handle, aim_offset); } + } + static readonly int aim_offset = (int)Marshal.OffsetOf ("aim"); + + public SoundIOFormat CurrentFormat { + get { return (SoundIOFormat) Marshal.ReadInt32 (handle, current_format_offset); } + } + static readonly int current_format_offset = (int)Marshal.OffsetOf ("current_format"); + + public SoundIOChannelLayout CurrentLayout { + get { return new SoundIOChannelLayout ((IntPtr) handle + current_layout_offset); + } + } + static readonly int current_layout_offset = (int)Marshal.OffsetOf ("current_layout"); + + public int FormatCount { + get { return Marshal.ReadInt32 (handle, format_count_offset); } + } + static readonly int format_count_offset = (int)Marshal.OffsetOf ("format_count"); + + public IEnumerable Formats { + get { + var ptr = Marshal.ReadIntPtr (handle, formats_offset); + for (int i = 0; i < FormatCount; i++) + yield return (SoundIOFormat) Marshal.ReadInt32 (ptr, i); + } + } + static readonly int formats_offset = (int)Marshal.OffsetOf ("formats"); + + public string Id { + get { return Marshal.PtrToStringAnsi (Marshal.ReadIntPtr (handle, id_offset)); } + } + static readonly int id_offset = (int)Marshal.OffsetOf ("id"); + + public bool IsRaw { + get { return Marshal.ReadInt32 (handle, is_raw_offset) != 0; } + } + static readonly int is_raw_offset = (int)Marshal.OffsetOf ("is_raw"); + + public int LayoutCount { + get { return Marshal.ReadInt32 (handle, layout_count_offset); } + } + static readonly int layout_count_offset = (int)Marshal.OffsetOf ("layout_count"); + + public IEnumerable Layouts { + get { + var ptr = Marshal.ReadIntPtr (handle, layouts_offset); + for (int i = 0; i < LayoutCount; i++) + yield return new SoundIOChannelLayout (ptr + i * Marshal.SizeOf ()); + } + } + static readonly int layouts_offset = (int) Marshal.OffsetOf ("layouts"); + + public string Name { + get { return Marshal.PtrToStringAnsi (Marshal.ReadIntPtr (handle, name_offset)); } + } + static readonly int name_offset = (int)Marshal.OffsetOf ("name"); + + public int ProbeError { + get { return Marshal.ReadInt32 (handle, probe_error_offset); } + } + static readonly int probe_error_offset = (int)Marshal.OffsetOf ("probe_error"); + + public int ReferenceCount { + get { return Marshal.ReadInt32 (handle, ref_count_offset); } + } + static readonly int ref_count_offset = (int)Marshal.OffsetOf ("ref_count"); + + public int SampleRateCount { + get { return Marshal.ReadInt32 (handle, sample_rate_count_offset); } + } + static readonly int sample_rate_count_offset = (int)Marshal.OffsetOf ("sample_rate_count"); + + public IEnumerable SampleRates { + get { + var ptr = Marshal.ReadIntPtr (handle, sample_rates_offset); + for (int i = 0; i < SampleRateCount; i++) + yield return new SoundIOSampleRateRange ( + Marshal.ReadInt32 (ptr, i * 2), + Marshal.ReadInt32 (ptr, i * 2 + 1)); + } + } + static readonly int sample_rates_offset = (int)Marshal.OffsetOf ("sample_rates"); + + public double SoftwareLatencyCurrent { + get { return MarshalEx.ReadDouble (handle, software_latency_current_offset); } + set { MarshalEx.WriteDouble (handle, software_latency_current_offset, value); } + } + static readonly int software_latency_current_offset = (int)Marshal.OffsetOf ("software_latency_current"); + + public double SoftwareLatencyMin { + get { return MarshalEx.ReadDouble (handle, software_latency_min_offset); } + set { MarshalEx.WriteDouble (handle, software_latency_min_offset, value); } + } + static readonly int software_latency_min_offset = (int)Marshal.OffsetOf ("software_latency_min"); + + public double SoftwareLatencyMax { + get { return MarshalEx.ReadDouble (handle, software_latency_max_offset); } + set { MarshalEx.WriteDouble (handle, software_latency_max_offset, value); } + } + static readonly int software_latency_max_offset = (int)Marshal.OffsetOf ("software_latency_max"); + + public SoundIO SoundIO { + get { return new SoundIO (Marshal.ReadIntPtr (handle, soundio_offset)); } + } + static readonly int soundio_offset = (int)Marshal.OffsetOf ("soundio"); + + // functions + + public void AddReference () + { + Natives.soundio_device_ref (handle); + } + + public void RemoveReference () + { + Natives.soundio_device_unref (handle); + } + + public void SortDeviceChannelLayouts () + { + Natives.soundio_device_sort_channel_layouts (handle); + } + + public static readonly SoundIOFormat S16NE = BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE; + public static readonly SoundIOFormat U16NE = BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE; + public static readonly SoundIOFormat S24NE = BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE; + public static readonly SoundIOFormat U24NE = BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE; + public static readonly SoundIOFormat S32NE = BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE; + public static readonly SoundIOFormat U32NE = BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE; + public static readonly SoundIOFormat Float32NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE; + public static readonly SoundIOFormat Float64NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE; + public static readonly SoundIOFormat S16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE; + public static readonly SoundIOFormat U16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE; + public static readonly SoundIOFormat S24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE; + public static readonly SoundIOFormat U24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE; + public static readonly SoundIOFormat S32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE; + public static readonly SoundIOFormat U32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE; + public static readonly SoundIOFormat Float32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE; + public static readonly SoundIOFormat Float64FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE; + + public bool SupportsFormat (SoundIOFormat format) + { + return Natives.soundio_device_supports_format (handle, (SoundIoFormat) format); + } + + public bool SupportsSampleRate (int sampleRate) + { + return Natives.soundio_device_supports_sample_rate (handle, sampleRate); + } + + public int GetNearestSampleRate (int sampleRate) + { + return Natives.soundio_device_nearest_sample_rate (handle, sampleRate); + } + + public SoundIOInStream CreateInStream () + { + return new SoundIOInStream (Natives.soundio_instream_create (handle)); + } + + public SoundIOOutStream CreateOutStream () + { + return new SoundIOOutStream (Natives.soundio_outstream_create (handle)); + } + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIODeviceAim.cs b/Ryujinx.Audio/Native/libsoundio/SoundIODeviceAim.cs new file mode 100644 index 0000000000..9cd45f36f8 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIODeviceAim.cs @@ -0,0 +1,9 @@ +using System; +namespace SoundIOSharp +{ + public enum SoundIODeviceAim // soundio.h (228, 6) + { + Input = 0, + Output = 1, + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOException.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOException.cs new file mode 100644 index 0000000000..ff6a0337b7 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOException.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public class SoundIOException : Exception + { + internal SoundIOException (SoundIoError errorCode) + : base (Marshal.PtrToStringAnsi (Natives.soundio_strerror ((int) errorCode))) + { + } + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOFormat.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOFormat.cs new file mode 100644 index 0000000000..59434e1e57 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOFormat.cs @@ -0,0 +1,26 @@ +using System; +namespace SoundIOSharp +{ + public enum SoundIOFormat + { + Invalid = 0, + S8 = 1, + U8 = 2, + S16LE = 3, + S16BE = 4, + U16LE = 5, + U16BE = 6, + S24LE = 7, + S24BE = 8, + U24LE = 9, + U24BE = 10, + S32LE = 11, + S32BE = 12, + U32LE = 13, + U32BE = 14, + Float32LE = 15, + Float32BE = 16, + Float64LE = 17, + Float64BE = 18, + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOInStream.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOInStream.cs new file mode 100644 index 0000000000..fb0b310450 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOInStream.cs @@ -0,0 +1,228 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public class SoundIOInStream : IDisposable + { + internal SoundIOInStream (Pointer handle) + { + this.handle = handle; + } + + Pointer handle; + + public void Dispose () + { + Natives.soundio_instream_destroy (handle); + } + + // Equality (based on handle) + + public override bool Equals (object other) + { + var d = other as SoundIOInStream; + return d != null && (this.handle == d.handle); + } + + public override int GetHashCode () + { + return (int)(IntPtr)handle; + } + + public static bool operator == (SoundIOInStream obj1, SoundIOInStream obj2) + { + return (object)obj1 == null ? (object)obj2 == null : obj1.Equals (obj2); + } + + public static bool operator != (SoundIOInStream obj1, SoundIOInStream obj2) + { + return (object)obj1 == null ? (object)obj2 != null : !obj1.Equals (obj2); + } + + // fields + + public SoundIODevice Device { + get { return new SoundIODevice (Marshal.ReadIntPtr (handle, device_offset)); } + } + static readonly int device_offset = (int)Marshal.OffsetOf ("device"); + + public SoundIOFormat Format { + get { return (SoundIOFormat) Marshal.ReadInt32 (handle, format_offset); } + set { Marshal.WriteInt32 (handle, format_offset, (int) value); } + } + static readonly int format_offset = (int)Marshal.OffsetOf ("format"); + + public int SampleRate { + get { return Marshal.ReadInt32 (handle, sample_rate_offset); } + set { Marshal.WriteInt32 (handle, sample_rate_offset, value); } + } + static readonly int sample_rate_offset = (int)Marshal.OffsetOf ("sample_rate"); + + public SoundIOChannelLayout Layout { + get { return new SoundIOChannelLayout ((IntPtr) handle + layout_offset); } + set { + unsafe { + Buffer.MemoryCopy ((void*) ((IntPtr) handle + layout_offset), (void*)value.Handle, + Marshal.SizeOf (), Marshal.SizeOf ()); + } + } + } + static readonly int layout_offset = (int)Marshal.OffsetOf ("layout"); + + + public double SoftwareLatency { + get { return MarshalEx.ReadDouble (handle, software_latency_offset); } + set { MarshalEx.WriteDouble (handle, software_latency_offset, value); } + } + static readonly int software_latency_offset = (int)Marshal.OffsetOf ("software_latency"); + + // error_callback + public Action ErrorCallback { + get { return error_callback; } + set { + error_callback = value; + error_callback_native = _ => error_callback (); + var ptr = Marshal.GetFunctionPointerForDelegate (error_callback_native); + Marshal.WriteIntPtr (handle, error_callback_offset, ptr); + } + } + static readonly int error_callback_offset = (int)Marshal.OffsetOf ("error_callback"); + Action error_callback; + delegate void error_callback_delegate (IntPtr handle); + error_callback_delegate error_callback_native; + + // read_callback + public Action ReadCallback { + get { return read_callback; } + set { + read_callback = value; + read_callback_native = (_, minFrameCount, maxFrameCount) => read_callback (minFrameCount, maxFrameCount); + var ptr = Marshal.GetFunctionPointerForDelegate (read_callback_native); + Marshal.WriteIntPtr (handle, read_callback_offset, ptr); + } + } + static readonly int read_callback_offset = (int)Marshal.OffsetOf ("read_callback"); + Action read_callback; + delegate void read_callback_delegate (IntPtr handle, int min, int max); + read_callback_delegate read_callback_native; + + // overflow_callback + public Action OverflowCallback { + get { return overflow_callback; } + set { + overflow_callback = value; + overflow_callback_native = _ => overflow_callback (); + var ptr = Marshal.GetFunctionPointerForDelegate (overflow_callback_native); + Marshal.WriteIntPtr (handle, overflow_callback_offset, ptr); + } + } + static readonly int overflow_callback_offset = (int)Marshal.OffsetOf ("overflow_callback"); + Action overflow_callback; + delegate void overflow_callback_delegate (IntPtr handle); + overflow_callback_delegate overflow_callback_native; + + // FIXME: this should be taken care in more centralized/decent manner... we don't want to write + // this kind of code anywhere we need string marshaling. + List allocated_hglobals = new List (); + + public string Name { + get { return Marshal.PtrToStringAnsi (Marshal.ReadIntPtr (handle, name_offset)); } + set { + unsafe { + var existing = Marshal.ReadIntPtr (handle, name_offset); + if (allocated_hglobals.Contains (existing)) { + allocated_hglobals.Remove (existing); + Marshal.FreeHGlobal (existing); + } + var ptr = Marshal.StringToHGlobalAnsi (value); + Marshal.WriteIntPtr (handle, name_offset, ptr); + allocated_hglobals.Add (ptr); + } + } + } + static readonly int name_offset = (int)Marshal.OffsetOf ("name"); + + public bool NonTerminalHint { + get { return Marshal.ReadInt32 (handle, non_terminal_hint_offset) != 0; } + } + static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf ("non_terminal_hint"); + + public int BytesPerFrame { + get { return Marshal.ReadInt32 (handle, bytes_per_frame_offset); } + } + static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf ("bytes_per_frame"); + + public int BytesPerSample { + get { return Marshal.ReadInt32 (handle, bytes_per_sample_offset); } + } + static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf ("bytes_per_sample"); + + public string LayoutErrorMessage { + get { + var code = (SoundIoError) Marshal.ReadInt32 (handle, layout_error_offset); + return code == SoundIoError.SoundIoErrorNone ? null : Marshal.PtrToStringAnsi (Natives.soundio_strerror ((int) code)); + } + } + static readonly int layout_error_offset = (int)Marshal.OffsetOf ("layout_error"); + + // functions + + public void Open () + { + var ret = (SoundIoError) Natives.soundio_instream_open (handle); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public void Start () + { + var ret = (SoundIoError)Natives.soundio_instream_start (handle); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public SoundIOChannelAreas BeginRead (ref int frameCount) + { + IntPtr ptrs = default (IntPtr); + int nativeFrameCount = frameCount; + unsafe { + var frameCountPtr = &nativeFrameCount; + var ptrptr = &ptrs; + var ret = (SoundIoError) Natives.soundio_instream_begin_read (handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr); + frameCount = *frameCountPtr; + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + return new SoundIOChannelAreas (ptrs, Layout.ChannelCount, frameCount); + } + } + + public void EndRead () + { + var ret = (SoundIoError) Natives.soundio_instream_end_read (handle); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public void Pause (bool pause) + { + var ret = (SoundIoError) Natives.soundio_instream_pause (handle, pause); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public double GetLatency () + { + unsafe { + double* dptr = null; + IntPtr p = new IntPtr (dptr); + var ret = (SoundIoError) Natives.soundio_instream_get_latency (handle, p); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + dptr = (double*) p; + return *dptr; + } + } + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs new file mode 100644 index 0000000000..0b77e1af84 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOOutStream.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace SoundIOSharp +{ + public class SoundIOOutStream : IDisposable + { + internal SoundIOOutStream (Pointer handle) + { + this.handle = handle; + } + + Pointer handle; + + public void Dispose () + { + Natives.soundio_outstream_destroy (handle); + } + // Equality (based on handle) + + public override bool Equals (object other) + { + var d = other as SoundIOOutStream; + return d != null && (this.handle == d.handle); + } + + public override int GetHashCode () + { + return (int)(IntPtr)handle; + } + + public static bool operator == (SoundIOOutStream obj1, SoundIOOutStream obj2) + { + return (object)obj1 == null ? (object)obj2 == null : obj1.Equals (obj2); + } + + public static bool operator != (SoundIOOutStream obj1, SoundIOOutStream obj2) + { + return (object)obj1 == null ? (object)obj2 != null : !obj1.Equals (obj2); + } + + // fields + + public SoundIODevice Device { + get { return new SoundIODevice (Marshal.ReadIntPtr (handle, device_offset)); } + } + static readonly int device_offset = (int)Marshal.OffsetOf ("device"); + + public SoundIOFormat Format { + get { return (SoundIOFormat) Marshal.ReadInt32 (handle, format_offset); } + set { Marshal.WriteInt32 (handle, format_offset, (int) value); } + } + static readonly int format_offset = (int)Marshal.OffsetOf ("format"); + + public int SampleRate { + get { return Marshal.ReadInt32 (handle, sample_rate_offset); } + set { Marshal.WriteInt32 (handle, sample_rate_offset, value); } + } + static readonly int sample_rate_offset = (int)Marshal.OffsetOf ("sample_rate"); + + + public SoundIOChannelLayout Layout { + get { unsafe { return new SoundIOChannelLayout ((IntPtr) ((void*) ((IntPtr) handle + layout_offset))); } } + set { + unsafe { + Buffer.MemoryCopy ((void*)((IntPtr)handle + layout_offset), (void*)value.Handle, + Marshal.SizeOf (), Marshal.SizeOf ()); + } + } + } + static readonly int layout_offset = (int)Marshal.OffsetOf ("layout"); + + public double SoftwareLatency { + get { return MarshalEx.ReadDouble (handle, software_latency_offset); } + set { MarshalEx.WriteDouble (handle, software_latency_offset, value); } + } + static readonly int software_latency_offset = (int)Marshal.OffsetOf ("software_latency"); + + // error_callback + public Action ErrorCallback { + get { return error_callback; } + set { + error_callback = value; + if (value == null) + error_callback_native = null; + else + error_callback_native = stream => error_callback (); + var ptr = Marshal.GetFunctionPointerForDelegate (error_callback_native); + Marshal.WriteIntPtr (handle, error_callback_offset, ptr); + } + } + static readonly int error_callback_offset = (int)Marshal.OffsetOf ("error_callback"); + Action error_callback; + delegate void error_callback_delegate (IntPtr handle); + error_callback_delegate error_callback_native; + + // write_callback + public Action WriteCallback { + get { return write_callback; } + set { + write_callback = value; + if (value == null) + write_callback_native = null; + else + write_callback_native = (h, frame_count_min, frame_count_max) => write_callback (frame_count_min, frame_count_max); + var ptr = Marshal.GetFunctionPointerForDelegate (write_callback_native); + Marshal.WriteIntPtr (handle, write_callback_offset, ptr); + } + } + static readonly int write_callback_offset = (int)Marshal.OffsetOf ("write_callback"); + Action write_callback; + delegate void write_callback_delegate (IntPtr handle, int min, int max); + write_callback_delegate write_callback_native; + + // underflow_callback + public Action UnderflowCallback { + get { return underflow_callback; } + set { + underflow_callback = value; + if (value == null) + underflow_callback_native = null; + else + underflow_callback_native = h => underflow_callback (); + var ptr = Marshal.GetFunctionPointerForDelegate (underflow_callback_native); + Marshal.WriteIntPtr (handle, underflow_callback_offset, ptr); + } + } + static readonly int underflow_callback_offset = (int)Marshal.OffsetOf ("underflow_callback"); + Action underflow_callback; + delegate void underflow_callback_delegate (IntPtr handle); + underflow_callback_delegate underflow_callback_native; + + // FIXME: this should be taken care in more centralized/decent manner... we don't want to write + // this kind of code anywhere we need string marshaling. + List allocated_hglobals = new List (); + + public string Name { + get { return Marshal.PtrToStringAnsi (Marshal.ReadIntPtr (handle, name_offset)); } + set { + unsafe { + var existing = Marshal.ReadIntPtr (handle, name_offset); + if (allocated_hglobals.Contains (existing)) { + allocated_hglobals.Remove (existing); + Marshal.FreeHGlobal (existing); + } + var ptr = Marshal.StringToHGlobalAnsi (value); + Marshal.WriteIntPtr (handle, name_offset, ptr); + allocated_hglobals.Add (ptr); + } + } + } + static readonly int name_offset = (int)Marshal.OffsetOf ("name"); + + public bool NonTerminalHint { + get { return Marshal.ReadInt32 (handle, non_terminal_hint_offset) != 0; } + } + static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf ("non_terminal_hint"); + + public int BytesPerFrame { + get { return Marshal.ReadInt32 (handle, bytes_per_frame_offset); } + } + static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf ("bytes_per_frame"); + + public int BytesPerSample { + get { return Marshal.ReadInt32 (handle, bytes_per_sample_offset); } + } + static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf ("bytes_per_sample"); + + public string LayoutErrorMessage { + get { + var code = (SoundIoError) Marshal.ReadInt32 (handle, layout_error_offset); + return code == SoundIoError.SoundIoErrorNone ? null : Marshal.PtrToStringAnsi (Natives.soundio_strerror ((int) code)); + } + } + static readonly int layout_error_offset = (int)Marshal.OffsetOf ("layout_error"); + + // functions + + public void Open () + { + var ret = (SoundIoError) Natives.soundio_outstream_open (handle); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public void Start () + { + var ret = (SoundIoError)Natives.soundio_outstream_start (handle); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public SoundIOChannelAreas BeginWrite (ref int frameCount) + { + IntPtr ptrs = default (IntPtr); + int nativeFrameCount = frameCount; + unsafe { + var frameCountPtr = &nativeFrameCount; + var ptrptr = &ptrs; + var ret = (SoundIoError)Natives.soundio_outstream_begin_write (handle, (IntPtr) ptrptr, (IntPtr) frameCountPtr); + frameCount = *frameCountPtr; + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + return new SoundIOChannelAreas (ptrs, Layout.ChannelCount, frameCount); + } + } + + public void EndWrite () + { + var ret = (SoundIoError) Natives.soundio_outstream_end_write (handle); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public void ClearBuffer () + { + Natives.soundio_outstream_clear_buffer (handle); + } + + public void Pause (bool pause) + { + var ret = (SoundIoError) Natives.soundio_outstream_pause (handle, pause); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + } + + public double GetLatency () + { + unsafe { + double* dptr = null; + IntPtr p = new IntPtr (dptr); + var ret = (SoundIoError) Natives.soundio_outstream_get_latency (handle, p); + if (ret != SoundIoError.SoundIoErrorNone) + throw new SoundIOException (ret); + dptr = (double*) p; + return *dptr; + } + } + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIORingBuffer.cs b/Ryujinx.Audio/Native/libsoundio/SoundIORingBuffer.cs new file mode 100644 index 0000000000..63d796fd5b --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIORingBuffer.cs @@ -0,0 +1,61 @@ +using System; +namespace SoundIOSharp +{ + public class SoundIORingBuffer : IDisposable + { + internal SoundIORingBuffer (IntPtr handle) + { + this.handle = handle; + } + + IntPtr handle; + + public int Capacity { + get { return Natives.soundio_ring_buffer_capacity (handle); } + } + + public void Clear () + { + Natives.soundio_ring_buffer_clear (handle); + } + + public void Dispose () + { + Natives.soundio_ring_buffer_destroy (handle); + } + + public int FillCount { + get { + return Natives.soundio_ring_buffer_fill_count (handle); + } + } + + public int FreeCount { + get { + return Natives.soundio_ring_buffer_free_count (handle); + } + } + + public IntPtr ReadPointer { + get { + return Natives.soundio_ring_buffer_read_ptr (handle); + } + } + + public IntPtr WritePointer { + get { + return Natives.soundio_ring_buffer_write_ptr (handle); + } + } + + public void AdvanceReadPointer (int count) + { + Natives.soundio_ring_buffer_advance_read_ptr (handle, count); + } + + public void AdvanceWritePointer (int count) + { + Natives.soundio_ring_buffer_advance_write_ptr (handle, count); + } + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/SoundIOSampleRateRange.cs b/Ryujinx.Audio/Native/libsoundio/SoundIOSampleRateRange.cs new file mode 100644 index 0000000000..28fee4585d --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/SoundIOSampleRateRange.cs @@ -0,0 +1,15 @@ +using System; +namespace SoundIOSharp +{ + public struct SoundIOSampleRateRange + { + internal SoundIOSampleRateRange (int min, int max) + { + Min = min; + Max = max; + } + + public readonly int Min; + public readonly int Max; + } +} diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dll new file mode 100644 index 0000000000000000000000000000000000000000..53a83f0f4525466728ec9bf37215f1304a20ee9b GIT binary patch literal 370899 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

KhY6}`N z=Wm$=y2$+-f8T7-{d~R23VT7`;NQj$?)A4EC}H<#KF-L$?*u3>Zu#~9fAc#={+9oY z3=H4^xCRM;^PL~QzwP{R@Q1wSp_2 z2aO!{P6j3L)^E^q0DLqEe;;T#5@bAo>m1O+kNcn^pz{{kMK26~{r}(X4BCU=2kK9Q zhNyKxO`~rmDlbDoM#?)d;BfxepJ1(!(l1z`9yCV=Dh)ux9^K9yD?v+H`CCClWZ#ak zb$j#hx3v8G{~r{NEuaN-pi)5_lxEld{Qn<(=npJDzd3Vq@VA1x=AeNLP(EV((CI9I z5?`P}lo!Q6|Nn13##s6ee6^~5PL9%Ga5*OcUMJcON+2Bkt=izQb<08FzZNvI zxEN_X3OxUj;nM9Sa9h)(^D<}{x&u7M)*GT?0J>kt2fRu3c8yAaM>puEzD^ev0}v|$ z+&%!eAwVpJ7f#>*|99zjQStHUJm%Q=gQ4O7|B~QtAJBPpH$6HJy-)%b5WH~z3jYAb zFjDw{hMCSuLaw070L=w~+l$AY1VG!A9Cv`omudh1|A!cP17xH4?8v=5C@gopq+)>Y0Xs% z4E(L&Glr7$cs!F2xLO|d#~?+-l=zGC+1 zWtrr`c)&yRVDmu(kJd|$9ULB=7fYA7-mbIo>3ra!`GfIB>w!8QP?6tx$fMVXozbK7 z2O>Q;A7DJ@@ZZ40@?hy_kK{uhy*2ENt+z{fJT3o}vL9nN0F^1Ot#9j6Aci?I9(8Sf z>(ZIWQy1j;{V*sWmT)*Wzhwjowch6M_|3q;018GH28L1tqy|WxGB{>H^#wS+gYLIS zlCFQ|(jCVE3eA!nkKTC3W9+(~mUoKvJeprK^6%pYHJ2WO?s2rdQ~Cx;Tb&f#z(Zd_ zM>0SY6u80Eo4Nuz;RBlPf%K0&dn=e6k2x^vyad(PX%j$BWn^F|QHNNn{kqeo^8mPh z`2NbJ^8mP*bY#5n5;UR;uAiD8J9u>d=5L7uHQt&JGah4Cco_t$!sJ2CZw8mn8%RZV z>nBjF#*L@f1JsK15r zxBLYiqx1Zw=D+{{AxT-mr}G@>YDC8!>1k?>ejMFyI^e?!1UgSNKUClarGw^^uAr-nt-k#K@6zkf z=)&m71E1{!mEA83{`~*1d7;yd2Yi_eBt9_9pNbsE<^znNnMi2)1L|rJQT`nIgjxO= zLJA9T`Ev+dHGO=MC5lwWOn{d!pdFI0J10OifJ+xpdIJ?omPi^P^Vt=syGA7eR8}T{N=OF~3p}Ol0V*}Y<)^|69;mBh5Tz!63+Vhm$L1e@ zYYMxa1sp98)MrAft&5=X=FX3x?wQRCCOc4X-$lj7Me{)Gflg-u{tnPQQ@4*wglFeX zP=?t6T33iDaTk8VQsTylKtiJ;Iu6{w16fq+;{mf|t}WCONPUE4c=2D*VzQ4fq)`lD zIPL^mVZ{JREa1`S=4=7R*T-P>FFd@#<@uR^|Npyaf*k+S1)PT%AU1%*Kk>`||F7qO zn?n43s{jB0XS~qq%=1zjWxN}GzE@#4Xn=SJXwP-?&wS94v)v8?j-99Yryc|qF#Ow2 z^1B?~4eCBO9|TRhdUjrPW-ionG~3b@$6>e&2L;Wdxv<(DoU#s7U< zPnM{FhKdd|x+tD-=`ApDZ1`tc_OSVYIYhqm;qQA5D!Cz^a_LOt;co$*SJdss@$Co)e@i*2<=y;-$+h(fe;?>L9#Bi6GoKMO zP2czk)JSdo1!<*D1T{F1aPYSVfzDI^c7%n$6|{-PwKqrQzia0k$L2ST@G_h4!~g$| zy)G(@tq1C#xwgJ_?M?j;u9aQ*JzltKz5xY9(@f<3=&r3#9D8F_{?|z(`X8YBr1>@D zivys|$G`uVzJ_&PKpo}S!<{#v<$31~aBR&5 z2LnUvNyj~)X?4)`B_gh^Ph2{LJbI&;jyW+J@b{~MN^#5k#nUDr)lZI~MVglPi{60e zz@YtAP!i#9tp>I0+&DS-TMGaE{|}9Xm#P2$|95RYP~i>g$%Dg(zvULFl(M{Ev>fcq zm$LuCO87gsfLhlRL6V)fUgm=H3b?*;Y<&D5)bQ$z<9G>L_}=Zt0UGr|ijdYyuxH;e zx^zDB==Bk1^ys{W6#vbK7>_xD>i>(SpM8^`fZXfV8=}lu!V3$Dmmk5_UWSCnOBRUV z`J1PK%ms(h%Wt5%3S7T{np@D(l?-1x z&V$aqEJ7}wr@r4({r8`N!G-hG!B_H}hjxHCnimfK0CgW)LF@1Mx0k3`bbf4n2pYyJ zVQ=*200lsrW8;JW^=XccKmOO3DuGs1$a?gOW+j4_y4-jn_u>ElmvPMC$t6gAlh*hQ zG*re0>UaeUq&2?#Zx1S$4Vr)P@wXoZEd^{nP{EHBK8yz{fA0iut=|C}#`3uMLi6Im zhfJQAANVjH;P2Q3(!zLPCuqk4Si^Qo0<<9yq*d6J;aNut(0GR>nAo9QY54mCDR`WxJ=93XG{(;Ugcjjq5P^b3I znUkqR0bK3yfI1D}PSrh#JhWQ~8YHrO4;sGHD>rxvTIiBCfq%QRLR#wq&{Y(AB|?av z?aLQTZ@}yAE#LFEfVNwCG{0x$-^btlQ;)v|bVOtGPo0uOpq_Kp>;L~>-+Fiv|X3PH$)(pv zfX$`zhU3BC^50)}UOf1d$wl*Ki87>F)_DTdYHL2o==lA$WAhQlgTI(GPjy}_eeTM? z|B0*RDgHiCiRh|%3UuNK8v_G>D>Eo)n_n@ybOwWi22}pNIQ$B&b_YcrI4W97k@|a} zpal0{C%^jt|78(4XG6Q|jbIZ5pn)q;{|tQj2PA3&UL?Qz|KEi%nWxd41LQjo{{81b zp*80dXi%v6NCspDrz2>*=;jNzxBvgY1eI13prZvZpTqjQy`m>$!I{(W!fc-hT3?g(2XtOSFwe_=a5LoAPH=C3 z|I7dXUxLrf25p2d1u3+K%7epbK1jX}bPU)r#+ROd{{Qa|7I>)yI>r9`0Z{(*=zPK7 zq73Rvi-MhtdHyvhDnRG!ftDROf~Kco!&8mV!NdHbrVI?-3?99_jIp2zBG9S2pi&BS z#1g1ieY{1bhk*gK>gfM*$WaoYDyGv##l-Ny@fH=(%7?$8<_+|CNa)EQpw>!S=M?a% zw#QpkK+9`DLY-T{$KJm1Gy%_pmvHm%V`zS1(ENj`jPFH>DFed_M(~LqC9L2hra%pq z0{(5xKD|r8ykW7S z>T&V!JJIkfioabHl(4s5@<i;8H{`2X4^y0!zAf&-05F~5`mSFe!z!nO5VDW8k=9u-jZ@wd!k2bmYj;MrTs0opkR zx{SvIbodG=LOeUadVsFsd6EA0|NqxpA@yPNFILZPR|ftTFE-G*a=#e)Tb$UzET$4J z$L3$m{4ERF85p{!faBG(`6x%Hz{@gFA^_{}_E9l;A#VhZVUNyZ{4EziD{(pxzFJ|+5+}G$ji+q6jrcwLiqeG5#W>ET^T@^8YuX5KJx7RdRqh3 zQ+sjk)Bpc3H~s_Nbpv(}Xfg`wo(#|#g)sMk51#?s`??L{o^FWYn3hT7w9FDz*tmep zI0w*azMz=G)X$Amf91#j|6hVe-a!3vNQ`V?fy5AhOA#w*jjSsJC^>odf(|-z0o}87 z03-~G+Rm%|Ev#$|49!1F`CI<5fL1R)01Zv0eE$Fc<$6%j()@}EDX)Xf;cr_8D#5!U zA*TUyt8eE6@DQwL=hqGwm4X+k&;I{^S@0KpP7QYdfQ$eoEX=^K{PzFC#sjQLw$GBYqBr^A=v z^H&j(zJi6nbp@!*?)FhpaJ=iv08LjlDgiGVK&`oGP_d1w{wWUi3a|hFf2obB{wogk z1@Hg=fBE}2_{1ex5$1>^mOu@Rf*0!^|NsB;BxoZa%)dzK7vz;FP(H%qm8)O=|9?5* zH>h#{jlX3UC^dMI|3AUE^)`Qt8|aX%@5ew@ z8-MF5P&c#Nl>wBSS`RpOGV^z|Kz-)fdDYeM_R9*e^^o$n`GtWECx81Ba03z)Lbt%( z;&1$|S3%tm@WyK2*4rfwj@`_llO`e=85nw58hTwEFMx~&`4)8CIDEX*v%8eRqnFh; zlmT2Pcy|8vX#M8F@A3gO(lcELoPR;rQ+#}J>EZwXFW>(B{~s1QP)EMJ`3vd1T2PR< z8s3J+$-ZBpW=V*Ok56ZeiUUle00ZdY`T|fD@nVV&X!$y5CsB6*2V_-A;DPIT!!^@4J<3z!y@xJH))kd3tfE{48!d6tp@~^C#$ZH)Ovo0J$|8*{zwN$uvX=q;(3sbOwz} zx~PDT#{=bE(5Y0=it42|=s zVPN<^2i#nHnE)y7nOpSZ$R6H;r<2X zW@y0A{s9kWP!kChp3r392noTRs^C$+&o4gS`~UxC&UeJxZqN!5@cIbD+s!|v>jc3a zQpN+#hZ%bV#4S(NeCfRT;*KN(L#KpC^8pqQ(1NtX(D7o&<{#3a4NINZ>yp9R3~xi~ zTZncKaYxG&^`AR$zL*2n@4~;0&!h98N3R2mPp=!JYs*QO4o?=3&VwbQp3MgsUHP|p z{P$=+2$A3{eeKcuzl7PNlOJqnT>N3f+pjl6%m>$M9-Sw;J^x&O)ak+E(d+T2Gl1h| zh!p60dk56}Wt!hu7#`>@60t56@dV!^#orIwkKg=`(W5s?!lO4+!YBEe;U&+5Pnf)V zZInG3-*mJ0dN4Y6v#5Ok2r5gCb-JkN@VE3cfEst6owq$ZUl|^7H9U#v&wDl>W^wHH z{^Q!sXrA!xJm}i#^vCnyO9jW~hm1a!_lu9a zcFU-MI^JfUy)G($JS>kEYq)lYsQdxV-3YywftCL*-QaBCY`HsDAj2@jY4KINl?9qIfrJK>E+xd@2^FbCL%>$qrpUy*_j(73+uwm~ z2c6pD(R{=LrIidyP7~11F99{B`8BtIyBZ#yXB|7Q`gR_1?6&mjJl*-D`N4U|PDT*T z-u#1wzx|U4XvwPR%0LDNk7Sl655`0PAGvf}8lH4){;yES55AO%@c^i2Yk9o>+Y3i= zP`~e>W3T&v-`+rpgFje3X83eo^6WKf@#%a39WU$VfTZ0+EFPA}I=%iBzw^<&YV)dFqjT$+fdk#PQ%WR!9E5 zE)yl1Kgc_39`oru2^x#ico8THiVJAL>e~4Vbcl%tf9o$61_sw|M-k8fXDjHkQ=iV0 zFP4IqZ*{kTPuvHc1P|K64CR&b>ZH{~OOSFnqB0=medG zbkO7A0|m#<6CRw0K#L_n-5U*;PSC+nJ}Mf9CtNfSeBzJc-S5x9pliwk3eHdbk*p{D zLFWQQ@@@dr5it5hoe0R%PyAX3K&n%TD{yEqGIrgUmyoevK&j} z*E`DJmctBM&~^Z12&i5IjVQUOXt;EKfL*hy30{)qqT<2d!Nx{eKeLd7F?1_nkK z70|J$pceRX=sl**d%zb7aPqf*0WAxG@L2i#K7x2_z;Y~x2cW8$`CFE9GBEJ>T?KU! zAZl5TyQqKW#w-@&c?vdU817Fyajv*LFX25<>*P@GhNqPYMR;TbP(foh&k7nOi+R~}GIXMke40CdGo1;pW4-x|KAq>Q*!WvgI2jlm_ko%>;PC;_Sv4HO3=AI4 zd%&0dFu)3oV{8na8YSEyF1WDjG;rZG_5dy6co_p~{K!Mv*N(>>LHD9DynG63zsNIm z9s(72YW#5Df>Mu$Yi|fEgQMoLmydseTQ8vDFHmM`Jot~9fuZr|cXkE_{%HqZvi|-5 zAC}Qx&IGL&>+S(3=GK$^ePWyp49$DMTs~0Jg-R(N>+At1=guB*g75TEv3Uu)F#{C& zuw=8;8&ZJbOg3r!8fQQaIfqaD0^p<&!&?c`1CCZ#PH;*Q%?FEwA4=obJILRD4Ky#{ z!C&|2xQhyC<0=EF{sfI;cPt03d#q8>@JK#x?JC0GKONK}>*hV+3pPgNV0R1H7YAR+ zgNC#CK*KjKDmJYL`1_gIL4_cHYZWs{X{6{^FHqVHQPBXWHjOlXy{r7qvCIq%oj*Xs zMxa4$2A|IJ%?FrV4gYr@1;>x$KG0yLM{fb+F$P0W(lvOYCdk0x+xnls1vDN5x(!!U zkbwbw|6sR^3h4B`mR4raGLs+Op)4+)7hN?!IQBX)x@aD>ya-L8wZ`3Bz=cz<-{0nk ze@j%lOH^zeH7_~#I)Ta;XzybFLCX*P^AELLW+52Z0oU`eL6s!3A0Z)Cr(@Oa^E{<)V_o z=%SJUDqmk@3WDlw7ZpfB<=J_>^;?}1=mvw<|D8YTBrN~&`yJDCQAyA|4mC*g7-%fA z+eO6yG?{UrP8${o3)mPKKqW-0JE$lHEnw{J}L&?jv~;$2x!`Nh714RoJI-9=CATD+Bx9M#}XWy-?BJ1 zKb3c3eC5))2AmySI$yM2suKcbaB)xu?>ycOiKXrk6%WQkt(WTfA&KKB*n=+M$VmYW zeA|O+JW%|CI_Ld0pdG)EN`?utf)ZRHgUZ>~N8p6*1CGF5puxBoI{csn`xCT`8x)l< z_6UO$G z=9448)^Sk1zsntzHFQoiA7qAR{1@t`pf&@L4E)C2SAM&Q0WXRkpsW~|KFUW zqQUsm9W)aQN|)UwDhZ%Fw2l6P)}(}}M1YEw77iu`2B%&Tz1B;0+@MVCAP8y@A8&o@ z+xeHj^*`t~3P=otya#rzPv;WwRg)f_b5uZQ?z}t%TKd^}@P#NZ=z?4B7I2m>l?0XY zpmi7D7#K@=K$Saa*=hp^C^vlL@5li)jJYA%zf=%OEq_ZOD`;m$(M za`Q1phmO4epj>PXaSwk>6gy}?$N`VepS!p~s~=f;!F*rK_a2?c!9qTrKlxjlL2@s7 zK$nEPbTvExmhkL6{^A-B$frk}A;vL!bjPR|cy#^*O(6aL2pTf_<CMH+?!^Id&d<(E~0VJOn&D59|g7iz{ezJm@$~a0>ki%Ag)E#05bO0#IA+@XLGu z{{MfG2Db0GXXnw^-JpzH$_GuSjQo8X9H8R{9QVpIFfhEV23=+J_GK7o?88MR0u;|L zE53nF2MPhZ@WmbhaQg3D^XLD6S3`IJfaCkce^3VRJPs{>TFnqeCR3eoHzZNE9sngv z{=W7<;K5wb^bbe?+C~9+2@=!YDJljo-8m{YucbXYf5HOq#T-zJ4;E~l$9+H}EI&P( zk1@VD^x^;i-V6g!RJm9~ij4ZBu7*!Q_rHQl9|acBPCq987EchPUWAdq?=dTAfG_Mn zsN4v6Edr|VJS-3LxA208wgUcpbRKK?W$DrD_P^wL^C3n^MbsexQf}~K5f`)$@&b)= zw>~N1^|ky1+GqCgMLXy&9dO~m_%iM%C@+ITue(Mi09+dzAbB>8U*m*FZ#m;9{hV*5uLsV?|TU@|RCIQ(lXK=IfS+|dh z&1-(B642c<%3uYNhry>}9AoT+Xa-q`&9bonD7I}+0wwQmAC(Lb(2e)UeL7FQxXuMie_$cdg>5g&IUzM#fs3Y# ziUEI1J7{>W^C$Q;{cdQrbQzL%US|J<9uEmE&p;{S<->3P|2NmD*zh5R9JuBJwVM== zz2pF`v4a&fFBkv#{~ug58h(Qnjz}dU)Ld}VX8H2}e|L?F1E?thUL*gXk-udks1nOj z(SZ2-<$Tb>R!}>!*+oTz5t0UuF&KhZK>Bo^-wWzCAeXM-QqQMbc99FH<^Z?71Y%Si zkX#N<;Gg&fc+WdBFnn_4kCZw6S`=Czf8vik*nE%?S`&bpz^w;Lcpyz;tqY(=FetHu z8tiWWyCLmF%TuK%L8fhV1Sf>cpZFs#rt#|?D);@wA9EU1D)YCgd;^u!ddK*?@|hVJ z_**kU%_GBaui5#xIsNzOyk=wI(Hr)^iI1BcJSfIidbmYE4srO4&(1Hli?Olckd^%r(ax=Kpe6g6FfdO2+LP}g8ewQDv zh6g-5kG$w)2bHg%K;v_r$6xGz11<)9JD-A&^7#0Iiw9I<)vs>;Ysufj0mK!WW!BPg6-?gh;*Ldy-~&!Ayne~uU6DW_f!0gujOp2_Yap1t7$KD{ihpz)3u zcA^XnUlwMgmCDQGKR_3$MjQa80si*G;MHP!7eF;LXu&MVB2dIa9Q_is&8YdW zCFsHjP=(6h0%`&^A7z9F3@H44JKsZ6{Yz$uZ)`wY1@b=s|KGg_+(T*o&);|WGssc_ z)<1Tj){FozgpNG&i9hnhC;kZDFLsa)%PCMl_XL)HZs)K4pz+_A@*n^I2U*??NiCq5 zu>y6)wz+|Fss^a+4S3lOnn(n9`d*rY_wv@qyBdB2#YO##=6VSR@Qvv%{H`BfN`QjY z9dr_Bs}N{J+wj{<&|K~@HcOBt&BqvhJKyiJgf{yEUh;yriPTFl@VBP_1yyk16JD-> zhPz=Y12h-|YKa*@)7xrLFYX5ef8S&V28QOpmL*GEI-f$qtNEzm%M8#|55%RQ14mJU z8gvDnYwNeVViX-|V2cm7R_3A|hd zx*-Eno3H%>PORUKGV-^A&QgFl>*W>DE)96P*3x&w^ydqv)81tq^6tAr`L^TKd5Kdc~SHF3&I>U6?)h|{dRYouserV9 z(<2$Q@jg((r&w7gLM?(^Z#{CiF^ zo;VCXx(mD>T%(&+WIAY2`D61Brjnf210`CZGYjNAx@CEGK#za;f4Q6W{WMS$hq2@x zNSP~WJ9Bj0;T@nFw4L?xG*B^E>hICZdt(}?aFdPP&cN{hvPUoPhiOnoJgB*vwc-E& ze{_iovqnD>u9aK2%`0@Y$fAC^v#e*+y zfck{3pc`oUTR?fT*|rp9Zf7}1XFN~I%H!^kB-a*rS*CWj6yuZ}5MR!sJUHnkPJZMW%u+?L6pl@Fz29Q%mP155|igjXy!75B%+* zvv@puMXq~vivE>lV0Z~SK?OG7VtC;71kC-zttUM?PnYO4S8{OH>l&VXu?$qAB?ohO z^s;78VPI&s-7#EURDB{t19#jf=Cg1Q#zGQe3G9K^P&C&@v8Qp{NhDYaN$4-_` z(7c()!Pg3w=jtDMfX>_aQ6g$x&QmMtk$kNA7-MHlyF_QX2q<)*=c63%cIW8zV0^&{ zT1xj0Xnqm^*X}?uXllC8IgX#>9O-PxH0(x zv_1uNcn()*gv4BRyDdLk{1e%@m=)C4)`MH*_^M+^VRnO#m z9-ZfWl0O)pbnLZ}bF{AI;h%rdk=d8yg*PbZLH&`-pmf!nA>omH-KRHYDuYMoOCQUp z#r(%X`IP}Q^W|xIru?Hv^Dzm}=A#mRy(Zf{dt>G@_*i}`PC}GF&Bs`}!~V2hDslDf zeC6Bu($(9YK!QIdo2jsANz_0TxPL0zh^A{*jXV0 z4xzaCSjQO9^76xm2VTELD*s^Vq4R=A=h@E7%@3IQTS3E22Y)kpbiQc4UE<}@ZF{@} zw4UpKsmM!E1#t0qiIGRQIq0w)Gmp;i-wrdDyaAnK*7@>99W2U^m1KH!zVzt)`a=5e z|Nj?%m!9+Jd;uw(;$sg(V)GzqHyp$q_h#Em6B!toO4oyyB2EIGzmmO)f#LOn37`R} zUePu)aQEF~Mz=`Q3rk20d2}A-Zw&>l`URa{q7fYjx&)!~7ij$HJ50qfk52ZI`yQYT z@eB;FXF=@s1lw!-33M`E>;Dq@X4^23-TbXapq1jiyp0n<_P^f9!0=Mx|Ns9GO(vjC z%Amx2yhQG}gG3<{0|P7tcCs!983nauqsMX9$?c$qBM zvGZzop2)%ftle%r-JTqbe|uvYEf3cTK>Hiu`AEyd_1|A?0y!IWIOh@mmZJ;|46Xlt zdL1PA`!+H#Fo35heHvKOCV*??;|}0$)V`J{O5cNaTl@67Nw{>rYJMc|*=y5Kk`12e zbTz!BdEqs)Z|64`7iCN@hs;5o~04z#mW|_pvd~>iz=Lc~8_0noRUa4j1w4)tLk?I+_o$ zbO-)%?T&(kjOHyL%|o7@7eMJD@Q>%g7YdHe4;Xzcua;l*usm00=i7POv-1?cJOilJ z<=7qc2ed8#p(Pke%eC^`9^Wr|^okgQ+FriBJ`-6yn_sfD{x1~->wn4O*e%%nnz8j# z$*0Z;Q1JlCJCO1pyw!-mQ|bTz|1bGL5p?o}C1^QH>j6-@2N$`=9l&ihScD%oy!{e% z_=Dkr*PGKOK2off7Yr}CXr8?Mf)_lWd+>pRkL6*H&SS-#uHDW*TsjY3e&yKfDeBUB;qn8{ zLmr(UJUB0a#?v^!gEkx<%|`^Hq`ISrO$;;0>e{x>*0F`#&#m}AR zJrpl^aQ=AR@6vg)`6YL$gyDae&I_GC!DfL?;5_j%|37H><-+9$h9}|TuXjO>d%Y1{ zvq8c~0bHnnx>|=lI+Mo+!;{dmXcwq%`s~QR=al2+S0DwwkqR!I0TQL@9?7Q; zK4Nujd~F0;aJSc|L&T+1zWJlP=BZ9GXbl0*Kdy$iKfCbnx$V+<^YUxg*4rh1;3#zI zyyT*J^YSB?&YPT<9WTG?jF14W40Y)YkdeI9e2B@l^<-y&NXbm+-YMLmE_kQ-E>i{u z1{cj+$i{=#$45xKK5BU2bvLwp106ip>&3{wO{nEkhd_xCsI|erE$}~RUl>TF^xbEa zBWS?w?Ff(7OW>S*nZE^e;gTViHwN83Dic8K+(qB62DQRVB7H!ERiIIQ3(w{sjQnjMLFcD{ z#+N{ig3b~Z1xN6PNzi@`P)8rG|14NPe+%fsXz+wJ4392^}FSj>n-EL+k{NNGO2L{&slXoHhX_=K*mPDE=`V zWdU|ni8R=%*GmjR^<@Nf|0L-2f&Z^3yx?JFU^vd8#Q>eOcv%fvQ@)Mav4at;;$=37 zFYf@mKPdv_N*5JB(9Tdl4{-Cv492~ zT~sW5dU=z&K#hrSpgT=KXGVK;{_^2>JqOzCuK;R2%z$`39<&7-eLq9<8xN0OR@pAl z!0t((&hws~S3US$et3cof;sjg6SNSx^N2?`Yq$YO#apo5{4I41kTvigt(QtleOtek zR2$y*X#G|y`#L>sg2xQc=3`7AorgS`Lm5064|`bN^Xv>|D0BDh`~Y&IOJ|CTf~V#U z&(5EqyB}-+f{wbXKj~rlxmcz3K&gV~ameAs9^ej;2dEVSssX{v`VDV`&bPVo`kCQ@ z*N>p--SB`%>o?G1T)UD7j-BrfZ-b8Xv3o5G>mPW`z;w%QghM)i`gZ;RIRrE&(0ZW$ z!s~gUUK^r439auuAn{}8(aq|m4~mMz;QVqO93(G56K+1eycfXDy4xPTJ}Pz~D?riV z+4;kV-}N+TJMKi#fEy?}z`la!LeP0n;5*t5q;-a;ib-l|3!^fg0mh6XfbK)rIM{L zDnV^5M3Mv>4Xs}cKe+&2u zif))W#?-(^?ju_09-SY2J4-=58<1;Z?RIGT0gYS2(_HfrR285R?biPupwn|o z6<>#fQiezJ1yB=uh6m%dh8h+Hh7xy}o6Jz%1lnTSdZ7M1|CdgnEgX=6B`En{HERXvfUy6nQBc|oN?So`EhsGorMaN=ujLSZub}iTD18b_ z?}E~+p!6&#-36tqpmY|Lj)Kx&P}&MgYe8u#D9r_>e=URB52bHG=~Ga86O>*Ar6+;u zZq`|?3=D|!z4JsnYX?XK8eh=TMwWqo5FNJ`NSVF~bn3R3?abRM5aFYi??kYV4KfDQYo1?op1{4e?*!Uv6MdbA#Z z8r20cN&{k&$A8sEUhrT6q(1WaubRmV@;E&HCM^YV|Eo4Z=`1K61*N^9v=)@+g3`a1 zK=i$W(zl@WEGV4?rK6y<6_l2O(p*sb*J6-4@ba+v4Y-O90F@L0pc*>@RAXzraNhX; zKWK?4v|NHZA6`Cm%QAsOwghxOK6s|Cz^$O={m zs;EIrC}HBXqHUq}fo8!#c7kYH*#$D!1!^D2Z!r5nBeWnnT3`<>_JQoCg`yvDnATzyLZAfEM0kNWo$sD93|(ptP{C z0BRq|Pss5H3Qt{y<2M9w*iZNpz;hBp49f=E~tGVzhFx*Ap58phx>5Y zhwMLU+IJF*edzH=P5Z85u@4k~Fgs!Xq*j`_Nx(i(yizlbK84x`G80>Rf%%VGVfvDQ zeK7w~%f2r-?1TA>+V-(2LE5{7!V{DasgY(Jq9N_@4KWaU0hBI?gYXODA+!OM-T)Q< z0HX=CL#`13`2lqL4u}Tf1(gv0gV-SK@DF1DhrbXScG(aUoE(ODud?`{)bz~alGGySoW$bd)M5f&E>A4V%gjr+ zQYg(!Ov*`BD9KkSPAyT$$V|^rC@9LzFUl;bR4BlUY$=z=WOg;}Z#wNw{wscv3=i7t|*8hQB&sYONkMVbuxr6mga zX$rZix%owvU>`$F26-nlFS8^wF(P zDXB$iIr-%bAj?xzK!KB*mzVyCVE+IA1M~m?3@rcu z=VT@o=a=TCWaeAJ0$u^x%~lGk#SF-bRErskGt=`@Qxq(8lQK&fO7l>-P>!J)NK(Pa z6{Z*};N*(X02M;0F)~5Z1QkHm1Qqf@G6yQ;1h>oB2+bU*0J1qyA!Ku)LQbv>X*v0c zB??f!4;mjXZw7ZRL_L)66s7oK%Ih{G!~%5=~IThs75toVzdG5`PHttdaQM8PLDt%SkNhanBb)6D_%Kum?8%=C;B2Dc!nOc8_&mU2!7 zl|l?|&QQ5z2;axel_4iBmBA@7IU8cGlMh2tYGM(@Sg$k5GMxvFcfE|q(Yns76zNn z5F7*%2OG)|l3xI`FT|OlB)-au%94SIYF#K1Te@9usqBnCx}I06-f4g)j+Lrf>;Aq04K)@j1knc-^UX}r2OI92 znZf|)fJ7J^bCWWQ^Yb#36~d9&5lHMvBz6=-gl?ol1elCu@JLNeDagprOD%>ZLnJ=f zdIsm5%;ao^kfOxoYzDXdqSVavJO!V`y!6t<^i+`UB2W{|R zOE75&)eO;X2+?i`(QgPbz!0>G15u7KFer$j_Ge&n5m51DD4hnS3!!u^lx~31Jy3ck zl%4~nSEA8tq5O?ddN-8b2c=Iy>B~_18kBwrr9VUI?@*dY9O8a?D6I^oEupk4l=gtq zAy7IFN+&|;Y$#m@r7NLy3zY7M(vzU{TqwN;N^gMD`=Rs&D18M=KZVj?q4ZBE%_#wK zzc`eZhSHi)+7e3JLTO(p9RsBkpmaWzhQ%L@ZonZv35WP19O9dCh#$rweieuKGaTZ7 zaEJ>_Lc#~;9~iBOL)-v|xFrs8XB^_5IK;zoh-cvtufrkUjYE7U4)LWp#5dv)--|>1 zBo6WOIK;2w5Pyn8{2LB&CMoRkD2zi~1&6pE4slZ);vP7}eQ<~e;Si6&As&ZAJOzh% z77pG^qyISgich9HbkZ>XRFRb;Mbz+ev259b>)nClrTXq0EBq-r8D zVVeDuOQ5F0$|o3Ik{@55Se67R8z#2K10o0G&q3oGLivL35ch-p z0P1^!#<4+jgrHl6#26SDctH{jkUdkN`3jIcXwDaO4!R&z4n#S~GcX8hGcYvRF)%2F zF)%RHGBB)|%)lVAhJnFl4FkiZ9SjT&yBQb)4l^)3IKsft0XiC~oRL9bBNGF|J0=E& zBvyz4V9l^GVFAz?@T(XsW*36&2Cd$0U|<0AL3UmcN_GU%3=9uo{XuAd5h99AHk3lj z#RUaWdOoCo3sd8pnU`L!TW)5eP*j>%l9`(d8@aLqjruV#IHsf&r4|<}s1_(>Bo-^= z2rsUa8NSpl(sA(Sp$@&CW$ivRxwq5L(lWA?u*=LZP8T()QVRXbNqcZ!Ws zlzS+M4@m*AaT&<@Nem1e(0nEXrA44LD7!**KuCDLWB`pLNkWPRh!j>5-JVE@xv+7k zj>8as)(@) z28Ic8NA_HEVqh>)`GqR&I~8~(sK{4c4n}hnD8Xxp|eBJ z)(YA6qAmgNA6e zfPYTg6||XBW|}99?UZa|d2qvbcG|5M`3-3^JQvR8ashP#I{Q75Zm!COz^Dl=_%I8D0Zg^}IxhE>|pH)wPL0$6c-J%YC zIt!2QJh0X6v~J0bEIS+T&Hv2Zl+T`Lv)Co$)F;*Gv8MB}RX59h`gifoPvH-|d2dg(1gr0!GvA*|CLD@5^5Ihb_J50g8^s_0 zetRUf>AX#1@vqyJPR(JSrr-7_eZGDpZPs`HpMF~xMH%HRT9P?yS97Dl%Ud4pnIh|Q z4~laws#(Cke(iOK4duaBYu`%$T6T4P#N9)e+~@CqUvMTSGS#tR?eeHC>wMZ5zdOAp zW97qM7OUs#`-S|c8+I#{z37w=oBAx@t6+h9zsjNB$@#b3RSOnv&wP5wZ7GZPv{RQ< z7fNkk<^Ob&n)_BmCD+JwUCE}nw`)0fO1jPT{CZt>4fp9se}8{WSefcLzat>6Qt;fg zhelbJvphFBOjKE!^{=2To$uJ=hAF#mcW3oRYGw znwQ+a=+^Y(!90@-n%5lnq#4erWA@wZ_l7@STd_~JE3ZO*i;MFlcFyPAclNE+%w03( zT=6o;hWW|KyA-$7EfcMn`=qYUv!f$0iuHqX$_>MqK$GvbVhjEXF6f!~;^V4H?ITvJ zyggR->wMN)$)#Z+n4lLJJlD66MO8Cx*M*RD;}tQkdGlQtRl8@)GM+ShF7SWmMrC%N zl^0`-*seQn@#VMMCB9>d!=j5#gqx{$x`tGv0 z-EFq%#AmjjLhs5ic{^;o_4Joic*F%Y{;Op!8dpdj+5OnHx_|OvE|2z}Z&imc z6f{+58b{B4Vd1;VZC>r~noB=FrgJ>k|K;buZlP}%_abWrM`@-)&*O@|`$`t@8^=ay ztLxv*IJZR5uch65$`j$(U*fzM@4vo!*R{QBmdV|VF-?_h`;LoiWcto!3Y-;Ocj1_$ zrpW!z3ithHdCh6Qd|muvOVnN6!l*juiCVY0dn($E3>Da3nect|pPlWDWtK7%fIo>WzDGG#@I=H2WQ!=Quo`;&bZFppipyLkIj_H z`RYz<+g~pJ_lD!*jYFIrp&PF_?YYHk5*E1ewm~t&gU+vSot7}h-WGV?dU<)$rwqk! znGQRbdX$LGeq^&@vSyA)zr4quV+?oIomR0$T%Y8v5*6sNq@XdjV%yV&Io-8S zd(NEIE_~Vfs|{2XmM(Ic#`yh{b)d<{RYGA)RlXf_TH;(h{X)tC|Keq)-_sNt{1+U( z`;O=1{@CNDMJ~Nr2YmWe=B+Q-nWrscacB2SiDZ|q(2LWJYFrOk6dCVp;&pRenc#2k zd8OdE%Yi+vtv4Pyx3RwCJN7a=^7#AT4s-QXeHXU!{B14Sb$f+~Po3(&+-5`KbhF7PmhV!jZQ{a_Lk*SN)T3bL^xqJ~Q50cet$h>C|?z`s*sg`$`<{?*?EIu$cP}Hk>_GO+X#s2BxKFaqcWn2)`TBv;*+%1v&o1re1&yD3D@c;;7d;?I$JlO$qn)el9vv{pUue z%Z6&>*A-D~76hlUEzjv);V_YJ;aBgL<4@C8-TWOqb-KeYm$!LyQ(x`4&vNsf)5d1U zjoeX!(-&8yD9xQZVd1r%p6e&?dC|JoI9m7o?WXjzM;-RxnJ*P{)-8GI<|BO>8HYUf zmHHdF?CV&5?9sn|M%{gGYj;|9-95VH|LjGp#GSjRc(3|r`ptLM$9IYcxNTOPVE0>D zoGo(tw02$mPO;Lm?lMe19f z_VBF{v->i4HfV<-$f+>*Y@5%t)WqhZ;GCHZ2mE)=Se3GO?FqT(3p|$GlZdg5xh(Fp zcBjm*+eSVcD-M0lSkAoZY(;tz*SA}KP4}LZ37oB1!1@xPGK zs8CEga76+Y0Gw&c8|EJ&bRnPk(-8ydmILdOX_{;$``Kl|Q4ct>M3ijE6 z;>RM_z^YhAM(dp5+NTVw7+lJ2LA__>_+%)PDfGy`D7b~0VHLwpt&fQvqB6!?4ZJs= ze-ZptCUVDuBOC(4W#89|B_*(J4#O%26Ipg! zr~+8}xrpZ8b7=BcI!^MNo}BKzve->SZ(->P+14Gr(R#1lCvjZrF^Xcm-iBs=3mRVw z%{&L=4@$Km(gJ;{91Pmp7bSxvg-S0xcU%pcwM-kTZmq0?D9Ym#p+pGEq4!D$=} zs~9qPw=kfYuZX5!8;!4o#=keAa*D>8OM(l&Gc?4v_HSgkx3>VKBJ%Jksd zzLFJE0^Pe9RxudxtY${za|wL&>sWt5aE2Sh0sliYC%dA#m*Wfn`nf_1-V;AC?Nz?f z90a;w0^|gk|Dw^l9oKZt6npW@zFrYc{R1@n zAENQMi#}Mav*N7p!ho<@@7|iN-q!HR%Kr2P!A?1b1OD6gA9;Zro?2A z`Dzxk+{)hH1C5n~w7|>*jgKSq>*G%?b(d)OofAE=mGgU`Ea-qZWO;t?gu)HeQ`}q2 z^@U${i6}{Do_KTn-~r#(xYTb)1J`>lU%>N6?C>T335Iq_eYyV1i_z*UK{WTtq47a? zS0me}i6##^BoSF&8cn_ft$gZ4}l}qhfMia9aVxDh8ixHx{hbn}o~<9Ylj{z6_dvSv0;78sFgauZ76vCBrAi3&{Kp zm%TcX`QQ^=kkW_A3CZ5dj*EiLFG1zcu`3rczh1SDloHsQ$-$tlyT3{b&AwHav&~&C zqy<{uaxiG?9T(<7ljla`KX@v&cipmcf_u#aoEQ!=Fq}loKMDRf%uhQZ%Gbt-uS(n+ z7X(+VWLR*LM}Z}Lt;FL6T>P$UmgcW6WWFh$@#}s04F5FeT}9#6wjHd6Txj-zuJ%F> z&j#hI_3VDq0_$`k?rq@wQEjweN}zi;q&&V6EAdrGf!ll4%@0Ax_IhYHqRF4K(!brN ze^IcH1=RkTG3y)}{|p*`0r!m1Ri@_!w-rL-JCxU=NV|8jbE~j+-e(b$m!PXzk=?f- z;`&N)xATHi|3K?&E%i|3@MuB{kFGu9KX*x2xGZv&zQ_D}is9NAev3HiPfiW7$@P~f zU6zZuTA4r1&eL__#oE$K8XY2U-(Ct&3gC0t$R4|{@?ZF}KP-jcero-7+pMc%Bf|3e z;)R3rg4fo0H;;#l9lE=BVn6#LO@5b2er}5<2DvKy z{QXccOgG!BcXQ3f$x8}9$*|_lz8kCPJfYy9*6GmaQ@$`vS=nM6?mA_ckkfCSlgpU) z{GR>sy_7)9TS)t@7cIY~x;3JJ!tY)Xz>fW8v;4}Uiam8eCly?>18^*OLp)1 znYpau1`}sZ@S9v)l2f#Lzkk}*Ms&|?cpG^BU($KO{cQ{jPBMF#9mt;lNipD@;3`oDhD=6Hdwr1O zk@DY*ojk9nRbCQo*JEJ#!NCzZ=i5F1lehg`X9{fgw@KM50J<0+qy<(UCuF1M&kKhB zk&^Wn1UF7$Sa6c}#D0dbjjE!I7X#V|G*AS{XvJoT(gpMf(<61_8sT186oG| zuVp-SnIxLKr0z%a4Mslgl4l~)0u%ip`SW0J^u~wBF9^0ZFsx$Od%Cb2Ek3|UQ6bq6 zn$rb22^L%MXCsX!`!1<=}~%xHW)kYPynF`~%}q4Al}_=0GB z(A~-)BVq0Xu|XKdR|9d7?BhiXA9XbTB((7W&^^Z>BVgujKVtIn$Hr2>9c?8$Ii2^N zzJb>M4Expg>F>b;r@r*fuPeD4UtB|zzlz3hbw3fT+H%x)y7bfDIg&rOEna_1U%52R ze`fgHxcX-^zD+T@rQ@HY;IJ`K{iwkS`DJ#Fi~q6K33|>dEMPYLcxVdqjis`auH5zO zQGRgX|GRoVSbGZOW)Mb~hk6$z1;*&|p!q?t1e8FRhYG_fba^(o41|F$4_ZGDk%5rt z^62a4(dA+7L5OY!26TDQx(JvYi1uZh|51lS-EU8EM$^LGec*$9K;kg0#@GJjsLdsx zU2dniW)-|;K{ww|HD-y-{l9(<3h5W!Rs=kNjaR_*3-k%^3V3wgt4-{@RMi!M=l=gJ zJY>EH`prAjVz7LTPmbl@s%t+QcsO@6? zKb}ht8y0f2Wwn>iQ#ivB?oj*1d&??qM&n*awY$2Tms-~E@!BzI0-y8NdAsYg<7Nh5 ztMc4gVYZk@b3fyWLqRKiq^zBKbUyPoO_u7|QU2MZA*0%-`;G-;*|Ah#cV}lS1r2w< zP)!9>JtI9>aR75Z!>Igd_>88H(flz` zFfvRKVqiEa%)lU%z{qexn1MlFgn>aPfssK$gn>a#gn_{%fsw&Lgn^+)gn_{Zq+W!9 zVH)T@(*#C_1tJU#3q=?h0umS*Hi$4VoEBkV2mzTR!oYA*gn=O4MHmU+YL>U-TK<*J`V5ksdV8}^eWM~j$V3;k&z)%1(UyOla zw-^IMMFJzk0Wk)KQ(_DZ4IuNy7#LoNF)(z1%oJl_(2!tY=mEJ?f`Q?L1OvkakQxaF z1}7>v~z7%UPQ864CY7^2h|7;F+5 z84}bO7>d;x7#tEA85-0W7-p(5Ft{W#GAvMIVECfOz~GU{$iSe^z@V+kzz~qg$Y7w! zz~HROz>t#2$Pl2(z~HXMz>ooAYcVinX)!RAfZU+Pz|f<`z)+FM$S^^Rfnl*0149kS zFIo%?C$$(D8WI^9E@&|@tkz**XaR}qFfbg}VPKd764zm1xTeFvFe8zX;eieV!#f=Y zhB+Yrg2Z$g7#4usrpv%^NSA?O3rM{#1H%_x28JCVcj_@Pxa%=6>;bt)kAY#e9s|Px zkokHH46pSV7%qU!(PLoPq0hi@1r(n83=IGD85r(>^cpZQv>7lk`~bPxfPrC`0Rsb1 z5+lO_0|o{$Lk0$sBt`}WLk5Nih71fcNsM4x&4__PCy9~4z=(k%!H9vuB#9ABw;3@o z*d#GBOfX_#m~OZS|~9UwQD zGBC88Gcfdk>@{a#IBU+puq26*;et5>!)tQ}h7BNd%o!LynlmtLNn&JRuwYq+-bqUu-Aft;R+}mEEpKRS}-u&0O_@4V5qWW zV0e(k$k1TPz_8Vlf#Cy4jU@xaBTEK`A0WFd85o4D7#RM5+-b$YaMhZDfgzcZ;ej;+ z!#isR29;z+h7Z;Z43Rbr3>wLd3<)+249zwS3Gft{*zC=~uqK(2;ea;-gRl<+!-ix=1_d7m1~nfBhAklV zJ`4;#J`4;yk{KBSd>9zIeHa+_fb{w>Fi86{FdP7>_hn!>?90G#0^|l?28M-x3=C(I z85uVCF)*n3GcY^=nd8sE5a!Ro@CM|5e+GsWe+Gsx$&3sI{tOKD{tOI1KyLPDU}*Jc zVE6+v-=BeDzCQy4Lkc6q0)GaErTz>IEGdi(8~hm~tr zaKWE};g&xGgFp%+!vlW?29W>;29Xp-2893y2Gsxt2ALE_27>?w2G;-v289$xhJXMD zhC2Za44@l=9|SNkum&69P_2A32@hJqjlh66zi3;`f} zgBTc?f*BZMKr7zT!QF$@d`K<39VFzkzAU^oI&6T`sZ8_U3O z0_5LV28OO!28IhDGh-PTUdA#o+(=<$_z=s$kQ~Rr@Brk;I0lBraSRMEK<30TFg%W9 zVE6!16UV@y9M8b;1LWp-28LwN{U@o63J|nDfmB9@1qloc zsYwhBG9Wcc3=9pK3=A5nj9@w{mw~}0m60JKmw|z!gMlF?m61W9gMs084+BFE=3hLdv`7_Ou;GF+I$z`#0}f#D9w{J9JasdE_^o`Bplmw};YE(61dR7Qpga~T-Y z<}omQ0g2CJV91`&z`&Bm$WSn!fnol91_qHdMur9R85lShFfhoZF)|1&U|k+sfPn#Y+qq5}BZI*L1_qM_3=9Toj0_G77#J3U_-Tv`8x}Ay@GoLuZ~*CD#K6G0 zlz|}x#9qq4uwoelLk>vIG6se@%NZC-Kz1!>Uf#LmX28JCV@zo3rOluez z_JHhK!@v-_hJoP#$jmhi4FA?JFdRu^WMEj!z%Y9)1H%Q7y=xg5ZmnftxRJ)l@L(+i zL-slbh6kWIHC2B{4U3_n2Ouz`U=c>@CjLpmcv z!3G9~vJIe>x{M4B8yFbcHZU-7q%$&1*ucQBaRUPbPdX#Rfej1{M>jAqh@>+zT-d5L2xn;964wlFZ*fY@6Y7;bH0VDLz1 zWO%TJf#J+n28IBT-mMG_7CRXjQb1~UGB614VqhpqXJk;=#lY}k4+BF(IwP3gzL$Yv z3djw685r{SF)%Cuxp^N0!?*nm3~SOE!HYM#4l^+90J-@v14Hx)28Ijij0_1U7#N;h zVqkazGUpNlgTiG7hCd+lFEcRAy3D}9lEKKZ;4%Zlp34jj0vU`92QD)(SYKse5XoR< zaJb6AQ2C01K_`Qeq2Uz+L(6Lh29FFzh6%437|y+BU6@A-Z3yJzGq-)$Y5kJc+bEP_MU-Z zLIxv4!g~gW)(;E}GeG8lU|{(2k%3`N2IzJ}1_t5J3=DfR7#S2kGccrlW?;CG!N^eX znStTn7Y2qa8H@}MzA!NS{>s4c1f>2O0|V!G28ItHd%rUIWHhM@lp3{OC9 zx&I6d8~-yfyaBas{xdL~`p>}7lZiTF$jHjXz-q*3z{tbSz~jVWz@ETXz#71EfawAQ z10xq31D6q-0c!$F0doM;0Y(-^29_oU21XVpRu*Q)07edGRt_T&3rUOtvd0Ky3mn7j z0Lg*Oa001kWMDz2L2@vRZZAwfHt`@v14a&31`a0{1Lg##0>%I=_QT8t(J-?>Y-|`N zk1agdm>Jldm<$*bu-Ji5AIKhjdhxj*n>#?}!7w(xOiTpEjLW^&hA3$*gQV$C6Af)(5mxu9TZh`S(G_s#T>?Dw@7#J8qY*;vg>;k1J zkQoA;3<6C&2HXi;1)KpK2iPvKJYf2OrXHjYgptiaW`pEF80H4_@PUbg)PS&x0E5a5 zRtC8a76yq5W(MI5CI-GuTn3y890lwFYzJ5_Fg*aLW0*dWdqH6e5IBUz4>0vG8sz3CXn26)#RwWN$ZSyfBj-Pmd(kl?BLkxWG(BQVDgRfLqUxma1bF0nC#V-Qq+ z;?f7w3&J4tVCeyr<`$uqBZ8c)f}41mp}FV)>jmZq;BpyeF35i5bPaMhD9+IRj4cho z)Pmdr!^rAjb`VR$%mC3KJ)m+A#$Lq00IwhUI2ibfpgw|?3+VQO^zyQ^@&<7-Lqqof z*x%^>1%=-sXx7DT!^A=1ZG;xy z=xRZJMQ^i!+>fpfMlSDTaa3iI~PI26r>M14}!u0TYUsl*MyW;LFopRRzUW^ z>Q|8agV5X$vJZq|egx?Q)g>S?5Qes^=sj@E_g^gxWaqI-jNs*{pu@*NSYrYsgULij z28T(E3>S157z`vB7(VDQfX789_%MLSNF20jc73cd^s0`j2UpA6t}RR#sn9%%-K z3lajKIkzpG$=AKTmVh*>oG6{=rJ&C0FB2fFfcSoGcatBX8@0Feh^~-kMms+ zV_=xz%>W)ZZBSrf*r3C}a6q1cp+JiPJQfKWYXprYZU8OdS7%@d&|(0OMLv*c0FR4; z#xX(TprA2N(AX$wEVDp~0X){JpvJ&(!IuF%p3>mYz>wh203J61jnf$TGl0idKw~Ns z{29RGG7EGeW6~2885kBQGB8}7#K`b^5+eh{WJU(n$&3tMlNlKbCNnbhPG)4-IGK^* z%49}{?~@rB*rzZu$WLKp(3`@@5IBXAp>zr(L+cbqhFMb>8TL+LWVkSek>Se}Mh4-j zj0{Fo85zQ+GBRXOWn`$E%E&NzDkH<%sf-MVr!q2JpUTMaWhx^B?=(gRwP}nD4$~MJ zLZ>k@6ij1e=$XdIuw)t|!`^9(4A-YIGJKiF$iOw7kwI}fBZI|sMuw2-j0{E785t%{ zXJlA4osr?lbVh~;(-|55O=n~fox#YUJA;wIbp|6t+zdvB<{69(D`zk=9Gk(&@L&cb z!|xf44B|5x8O&xfGK9`#WGI@+$S`3hBg5L6j12o`GBRA632qrNFflMQurRPPuraVR za4>K(a4~Q*@G$T)@GC@?58 zC^0BAs4%E9s4=KBXfS9pXfbFr=rHIq=rQOs7%&(z7%><#m@t?!m@$|$STI;JSTR^L z*f7{K*fH2MI50ReI3XPX2|mUYdSYZe=q!%Zyp;Il{L;J<26WN9#N1SbU}|wmd~RY% zazciO&QbI2aE(b~HXS zFD11Ct^=kdAF@jthnnJ&qWnrEJ)lEU<52?xsxvP&u?Q4LkRvPOi%^{d)>o98hHg}G zeo+ZpY!+jvft+Mg4BN>KOD9l^p$DB5qbCM*X*>=q%|me-{K!?fOVV;mi!FG@~@1#WS2VjeO$#~}GczB&DII)DMtnhPQ9Q(n zC>mg?6(uu(OhS%Y6dfoL3YWXEqYB*Z5Le_*Hq{wnudpfzlyRIs;1AKp^KJD4hVM zGoW+}l%4^lmqF=6Q2Gj#eg>t#L1~_q5PMXhG$gbc7_6ZD04N;?rE{Qk4V3PJ(o>-H z0w}!#N^gSFd!Y0wD18Y^-+|Ikp!6Fk{S8Vptb(|Y4@yfwX(cGF1Eo!%v>lZ8fYL!w zIu1%_K|Fi;Ee~VTyUCFkC_s zh1lkq!oVQFRC4gIEkpK{6qk$=S}}z8%9CCeOUg692s9R3}h_62!>_ zU)sR%6)xkP4?69>B$a{T2UrZEz&SrJB@=YkTv%dJW@1uKD#K4?S+p<|VD>C_adHm! zNli>~sw_!$tzcmAX7))YOQTeVF3{}j&r8y;;AcI2k zL5EvAXCxLe>}CcZb{CLZ1U}j@FF6(L$MX80Q;8raoOHfIUV@ir!XTSt36Ozd5{M0n_yFkT9t`tAVxf5%;1rwUT9KSu0J7dKGp8iAh+z#&a87D! z0mE7djS<++!3+$$SVBNof#fBYq=HfdDEToQWC_VBc26w{1K*5sjs>R$3>R5K^Kwzb z`3(!y3(&Lw7#QBcM8E;d!0?SFJTbGxEx#x@GcP?S)jugKHMxZ0KTCLGHYk+P(gPnW zENVjX;qk>_&k!FUpOYL9I!v%2F+DXttu!y0!H*$6J|!n7KQFZ+vxFg-AwE7cKM5km z5X}%DpPLk)Tv`;Ln^?iXP{9zNm|L744_3;siy=O>s33YWyN^~MVWaeX$%bY%!x_)MI{Uqn3EH8a`KZI7&bAd z6_l1FGi+y0D@sjeU^v8_25x9FFdS!2Pc2E!D`Q}I#SA*`Fef!RAH@5>oD0?aojEr( zH#xU}f#C<3RZz*mz`;@oYL+tauoR_2l!>u`?$b!jVPH^UDK04jtJegvz!!ipFld9= zIjMOJ40<3|9!P;7OIaGs&511K$;Dv#eJr5vY)WYXg9w9jKBUz1%quPdbrl#GTp676 z5m_3P=kgew^FtDgvwc%@-9VlzVsOq6%>xA+Bv&&qv@r%04d(7|ty&OD-ybxI|+CBLh0l&q+l%6dd5A#E=XD0n~E_ zVSZQuJ>RteO2foQ>0ufI2O#G&CO~N&=y{C>Xy-T%Q{NANZ+&230PRV>u^!}AMurd& zwV+_Zg9QN#4=glTv|y3I;)cZ^7AGvZu*6~MhNTM2CM;uEUaSLf5>{udE?8Z$x?y$4>Iti7tX{DC!)k^#9BUW^7#WT*Ffe2+Em(SB)s0mTR=rsD zVbzaS468X-3#^t{t*}~SwZUqO)efsYRtKz(SdD4Uiq#ud?^u0c^@-INR^M3tVD*dD zA6EYWn=P)z@mLeECSpy(nv69CYbw?>tm#;@V9ky-7uLL3!?0Fj zt-)H4wFzr0)=pTvV(o#oH`abw%dt*joyEF5!xE1r z0ZS5A7OZSoIbr33l^a$bSb1UPgOwjvGOQ9OV=vS-5cMa;;p*o=>WPz3 zd2|Y*zDf?J{sL6}9(09Jz6{tsUMev4P+>R)F>i$?On$;#i2b)A-Fdh=7z3g{!UCoq zR&OnaDL|tk_M6(n)T2o=K+KbHgvl!`fY>jBt`N$HsQ>N?Q||y(e-dx_9teY}7g!20 z-x06+m)S7&2`eG$v(Pt=L)`=M-^mh~dZ;j*g2=Bd#U&5%&)qVZ{DsvJ`@HbF?@K34 zy}|~Fdh~tSFb6=)f7}IAZva&fJJ$fF4n{-Vd!-wu9wrW>A?Bsegvl2`%|qYUj;em$ zY?%56qSU{e3sXMBICu)Q2pFslNbKF9JI#0;Uj3 zL)33N1yldw5+uC6aij-``qk%Q>K(2^)LV)|4Md|L>i^w`sc(1%QSXO0|5ZPRslV_V zqCN$7Fa}ohm43q1JA8+zufmaFAnsZEAEy4mFNk^}y#AfR!U!$zE(s#=7EMRQd1N%^GX<)I9S1gU;-?_#9o|{UsR%i zs*HsdEXe{<4PmpRD&b%U*;!IqkjlWs0UCp4KRZ6o56ezwg6)YzXi+}U<)vT@LR!r3AO-J2)_-?S6~Y;gYetId<{0}C??eN z9c-XLF<|qpa4s%lVD1ESP1u4{)18YMn7i2ASQ!{BKwOt%2Ig)s*9ydSEoNZu0ds9Y zT(@Ed=3X$@4#agYW?=3Ea~(k3;9>^melXXG%@u0#1h7pmY%WCx49pY3Zu4MsDKcbW zo&@Fuu(=c&F)&XCb0XMWii{bUr+_&LY%WD+49ru(oD4RXB69}jX<$wPTTo&J1M_q+ zr-Cggxq^Xt2AI>p7L-!Kz&sPo>0k>=s$gKA1?Eg(3o0&PV4e-;%wP*jDqvuq1LiDX z3o6cJV4e%+tY8aD$z))j2j*;G3$kEfo)2d4U< z4(9ye>;qk?&d3#(np~1!1iNIKf%yR20gwbUR00&cMfo`l%m=~R7O-%+q$ZW7!-Wrl zg;%g}f#w^WiW2jZGeYvg_8bPwY(SQAE6UFW%N+sB?SRO^vlUe1QLy9zG)cJLV_@kM zEDX%yo_^`KLSnVlj-Tp3uqK^tnA!$BFBwFh)MF|!MV(;LFbz`(@90=|5g zf!QY@%-_?6fpsP*YcnyjK}#WUQNYB+3Mq9!B@1(?kH4RLd_YJL1DheJ_<|T{X2QUB z2+V~T#QY0n5I0CI1G5C94kH5tw}+#ji;pV9*`H!*ccdiL1hRpsJP&@U}9hZvlzGouxQ-J6v)QFz{2s5 z6;!PG8X8$RG6*ocur{+XFtGXLmoqTCvc3Tq3E(?9ncY}#vx2H^ZirH5ch+z=1_nNm z@`Y>+4E&&@pxGh(jW9kx1D_Qu1H(ZWKc0bw<2V}wg8<06OdKpJ`K6#493}x4aFGt> zvT(d+V_*>Eh>!5~jSqGVbB%Wla&V6FuN^ENP70nD{zVBQYq z8i2W=0&xeJ?*QQ&GBEE1^8+A!BL?POV15FGZ_L2F8_X|&@J$$)_kj5g5WXn`^IkB2 z0wW}Gg9=7)VrOI!W?*7o0WJ_l+;U2S6U$N=m{)=ms0cWIAR?>4BBIHl9y>U@fGTCM z36czalUNxT9&kdl4AM++YOAx1^D7B1%c{7-U)&_C+3=Ro$ zL~f7>FmC`W6bG5hAa2hE%C#&UAXCLSn78s}f=qgR?CZ!gEk~0JINj^})A`%9*G+XFDYpr!p`f1slZy%CpJYKAE|hB@E2Rz+wVy!KF!%N#&5tBnIZ= zU{MKikh2xo9E*}NONtVUDnpA?ivsd9K{MnG%xA!AG}wYubJ7@?&w?Fcz`()*@&Gr~ zam?qy{^1UQ@z3)Gaf1p#V{Qfp?f_3;M|aow5TD?9XGb3&C(tG6%py`HU=>x|3=9(e z+zgQ7lo#Y_NWm&ul9=x7@9F0m;^Y|Y%D@6TUXp$4QEmnXDTHvmZ>Ud*XNX6TtD}o6 z=%ys;FvlQQzffO@dJYCAe?A5Vsj!fESKok;NC?v})W?T`5hUno#J~imO&FL#bdU)H z3z)WGU*&M42jX}dG6;a^P)|b!K?uW$K?uSy zVGsr}JS`YRKy;|51%oJv;prE`AO>QDf>`1p7PwNC0I@uRT!TY>LKq}L98m2k1!4p{ zy16F4UgAPW-k2>|(14#ErvvE@N*XtP`e#P;+H z0oTl`AP&eCV4fO?=jQ3-8sh2e%HROvIr;cIF*t%)!68Aej=l^|5Vn7iqq{4EGlT<@ zaCM3Ick*&|4qJ&hgj_*FAfsSv+&}`(ZVc{V+9%l4g~0>F408<%_Vo92b%}>s z=P45w5)X>Ec#w96|NJ0+n5%P$e-HzM0Ehz#2?hla+bJ^0)s2C{0mKe+4R#F*1Es45 z5ZBYk$JO1@hvEN!B%ZHhu(zuV)YOp309TLz!+(AT=J#3Q1*i@cL_;N zVqjjXz|GIVz|6t{(k&nF2`U0W6YQxepcNyDMX9b8pk>nOsj%XKNq_~^z+$fCXJAlp zNi8nP%md#T$iO7P0v2e zV%KSqT_K4@=^)=oLac!-j$neQU{Eb6N{vqjham%_9jHMB>mvI?M>81b7zi*hsDZ|w z867}OkhnTOv?Hw}&cGloz`&rv7{tuQz{sE}1(mQ92MaP4Gk{7rP}!mt?;I2o4=Sb@ zxLQHIz5u9FYv`~GDK;Het#K2&<1FFUys>W83fx!r}wjwnzg@Lgb zq*8;Kfx-9$RHdgl1B2#!C?^CO7-b9$40+OEd-5?H7wBBc)|jU4Kq;a z7zjaP1n!Q~R*)7@2uZQRLdZk`8bYMg3c1r^*05oD@i&|nN?G}2&+O~ zoGZ@2a8ZbX!HO}+iZPHuJp(EZ3d$D{t&IK*nhj84kaDO}1_ssmc$b_U-^9#3=b(^a z(8B)G0tUwQpcFj;svdTs1LGVqQCNyz4@%K?tgsZl9+aZ5fw|jEYiA zz}rffg(0y3vO!%7$^mH*6Txn%HB=4AVk;bK zLZE6u=ENeaVKjic%{U9HWFg2iRZtGdXT2!u4Wa7QCm|^TogAmZ7zFVz+(`BLP$eLj zT}CklW~=%ds1lIFzF}3Oc@U}uJJ=6?P2{}^~9RAFo8G|$!1DQWF25K-yFn?x@ zU@&GDfyCJ+P^#d7a`uWdFxbgMIUoZ-h4sV;HVwu=mWe4-SU|cbX0S0Z*vH2g6y+z! zCzg~XCTD=k2R%^vz{$)2y{OkAJ|0wQm8Hgm_i&UJfasD`2F``zVhju_pehCB~ z85q@4#2BEwDh5V%5RVU3vrL04O9m;^=!Pmwj;~^1)P(X!kP;od@rHrX092+s#6y?Rfs1)Vun>5_IXMr)A6IhiG(YaLvni{qda_W?$?y$lQtAk4YW4U`-}m~(?Gc)1;z;NH*ZHj{yY z0faf*+|DvEFn}}2^tv0Jyr#}GK!5Its@8;-(#A}3L10A(-Gu^ zcLYJ|nHiY+Sr`~N5FJ4f6Ved`u|OR`Fm1xX45C3DK`?E>zzU*49YGKc=?H>YpbiBC z2Z)8}2!eRfjv$B$=?H>Y@Qxse1MdieIPi`jhy&^ff@p9@5W+BG5P~pF7=%F#P)86% zgFAvC2B`Zd24aA^U<~3Q7NjExV!=CtAP%@A2x1_01VLO_M-aqD=?H=ZpdCR76Ved` zv0)uS5F6eR1aUyFfOG^wJVZwj!~=J~96>B-&&&zJhIRxY9JG!gL=dGT2oeGr1@8!g z1V9}@Fb(Mlf|y|#9YHR%jv$B+?+Ai8u#O;z4eJPk*hn2g5Es!AMB;(^ec+BDNDNCy zkX8HyXkZ#r%tG=z1Eb<(&}1<*&o2j+nV`1hoNcTO3_RdP)DRV6oD2-S5IxXhTp6T< zX<`CY6kLw0K*Tcmic(9;5_1?BK|AspPJ>3kM4(pWvN13SXhJy^Yzz!$tPBi-j6sYR z4E&~0VUQYrHzbZgER@sE#=uYl(ZE#9AjlZVG%-PtF@kAg1_OTY) zL_p(cJE6+vuz-C7YC|wEh{7s4Xpl*Of`^kCcC9m98XR_#5Xlbc9nWH*cnM--U=Rl} z7$oB3Gjj`I<}onJq=Tv$4rew72FdFT3=AO5x|bib{TGU|;}YMrq0C3=9k) z%(_krwqzYfGm0^QT1zYpKR^Z4(3p8nPfiBa#CpjL=!=+EvyfF^F&C5+rDUe1f$E2d zkD!Ta`prHQRe|A|P&oveyJkbHq-;PH+ti=)$;FT1J z1Fxh&9C#%K;(#hC5Dl)RAPgf0Aqc~SK^VjURZ<`tTuFf#;7SU_096kR;vg2Jk^-^d zl@y2ruB1Q=q)H0Jg;i1@HcBN05`b1x5GJIO0* z!r%yE!73>T8(K+0IB1m=L=dHt0ttbPf>%-?0Z=6crXiISh>2NAaiUdHAU?d30&!rK z6o?J0q(E$>N(#h9R8mMhcqIiAL#d=dMJjkxHY4{QMg|71`1q9k`1G9oq{N)~l#=|S zVg^PLSY|hcS5%i+7#O$<7#PJs3{K_++>j;G3@jX=l#F5sXs)}Mfl)yKx=b+?Zip?Y zw#zFh0okDx!N|bi%LO_`l7Uef#97J3z`*MR)wGwDfq{=Ph?5!S6b1%|Lv;e9I z)RzDm&NwFeN0%ni_vpdiT6lUW%+$F#)9g9eD> zeLRCh;^P?@c{*4b7=+{FT|)eW;DWp$K@n(;1*KR+7#X5iK`V&_*oI68BrmZ9-1XH< zW_ZC$Rsqg5RLW~+w1OIRs~_XkOQ0?Zo`RYcUQmP7Gchp5urM&NAPQ;_6H-uvSfGL$ zOq(z;gJ@7e4W=y^SV1(Xpa#*9f*Qnv6x1LVqM!!xpanIE2`Q*SEOjR zgE*jq8bpH&Y6!!KK?uSyVGsr}Km|331{c&I2DqRGF+fEmgE)ucQEg9M-jHG~N%s6lL4K@DQV3u+JtF4G}~ss6j#?qu>QKNB~q&gK0=X4Ps&z)NE)4HHZ%{s6iZ9K@DQV3ThA= zsh|dN5d}384_;7%#E=VWcF?uGETB}#GIs$e5n@apv7k>LAq#*fk2t_)%m<}mP8?%j z%)FOb7#KL2CqNs-Tp+tJ6@y2?nEC#L)y{yb<>p`omFNi7Xyakb-1@8_v$sHN3oejv z5UR_PlXHqPOF)xapwTmC9*F78T`PK2)6WNHTUIar}9Qy-Y?0OAH0GcfgoxlXLmHJeNmz&5$Cg4S#@ zO$58mgB7%9lW7u|6Tk{uv&l3W%!yzHt=VLn0_G&Jg4S#@O$Bo@SV3zxnWlj`1+1Vo zn@rQeoC;RZnoXt|U`_)oXw4?mOfaW|6|`oPX%?6>ffcl7lW8`XGlLbhW|L_Sn6rQt zv}TiOE|{}|6|`oPX&#uffi=j2foVRNy@NH#l7VRfn0m!gBR`426CO1})0UQiG5T#7+tdl`2F+s}jf|dq?u483q5Crkx z!ub3Qf}ld_FN`10fNkL>Qx01U2Ll7RXn`%2Km zHqb<~0xQbGO{Qu#P-h-b>cv>D8UF5G0=0_MRMZZd5Jb72cNnYMwsu!WmU+reD;!cC?fU_N}|CeuzZ zAHHytX&0CeU%1J%8_b6<++^AV=EE0mGVKNPCon=%EU0u80;OO^1`!4(rWN2)Q2@4Z zlW8S5feL`*2fT2TX%$$6zXY^!6P)HBaxyT0O^{^Zf$gMVI?e{Frg;L81YDRH7+B(i zLj6Ds%;Mcb{hUGTGZ}c8PP4^uF)*-#&%A-R6wZKC1_P^WUJ6|7ELe<%6>_QzC`~dj zodb(;Fz^Imn9OvZt&EF-ftiDei-AE9)Z_2rf;yFtfd^^vH)P8S(>zX4V~7WNSvXW? z0q1_OK_LAsNR6cce^4-N&%ug3=GmR_hsJTWnf^M7$BXTpI1_ppA+EBz?eOgm4SgVSUNK| z9lRzNBEURxN4<1vaYDz^Y>)q);!(GJtfAY%>{S_Bc|WOh)2 zj{eDbL#1|sr2^RmJ3AU0Y8Vs)pyKmEcMS`I;uREj;B3#xpacqRY4_lGcxM5cpT-gn>~PqFMo}S`~8k5QJoPU}U(-2I{Z&a}1f$z>=Ke;?e@W zWCrvd;!`-t>Z>xBX6BU?loZ96Ffe(8GC9$Mg)AKBKyyJXlx{F&<$!D@VqlhtWn^Gr z29KpNH*0{{;8rRFb1z5&J{1Jpqr^P38PvSQ(<+7-3~dF2JjV=a=d%6+>12cMLt^b? z1alD$Th=FxAk}#0m28+87}(jcEuLo;k%|OWgBIXO;=$=SsWtWsb(3GU#`s#O29sMMl-x6GUz23Bb)kYg3N zL8rVWCYPiZWfqrYCKofX%77(Q7}&6TfK^rs#E46FuVzd;vXGfIKhh4RG5BQKF+ zU!z`$z)W`dl=3)*YN7_^0vfq}08G%3Qs7y?qwS5VBr z7|IBmu4myWUmGo( zU@QATR!Z_RFbGbCmXa4iJM)EVnHd;hM-64dO?}A1z#u$>nSmjQmw`ceF*5_hT@Yg( zGXp~g9|MEPPG$xMkz>pZ45AmHrl~>og2wg0IzRJ4b?WdlFbIQm2Jka5h@q<&2N~7L z&%huKGVUN~883q%JM`=ZuoETX<4baiQqv*9Pz=gd0#JF_R&~ZXlB^62l92E$1_d8u z5NMTqMG9zXn=43wli35hxJ)WOz9I#*$juER9sm`WhOTr2ovqHO*$PVR%&aGOBlRSb;A5OD{%IB2b1 zX)$Q-$n+U_5K{qaiA)GH1A{DhF}QCC1EVh}6r@oTAJY1~TxB*;O%$L4aiI*z8i-?( z85r|GJWgiVxk3=}6b8l(5OE8bV<1aOKoeRyCB-R4`6ZxZ+mlln7&n6~=VaD^9#ABW zs<1K-rfv&FoeEZU#ijW$bz33o6rk$lf*BbYK$uaU;XY{Kk3kW%4{B&ET>!6VOsz=O zOJ+FCNmg}6bnV4NalHjA$~dw?!OT$zUTF}=$iUD9T37&D*v7!X0b57PRRm7;0?_0E z5wBukPt}a!86Ip2A|{vsb^+jDrR9|U`0%Ff|!s= zP7n(;$qA-S7??pcXp$35TQIPKXwW1lh=xpZf>@AAP7n(*$qC{?Cpke(C(x#HZV(GT z$qC}XCpkeJ_#`KY<7vns0HVQ@oDhZ)gAjya!XOM{fF?OXG$+ zXqPaP#485S5LP_w@*RW>sA7R06~hEqE`J5o?JCZ!N{ugJU{+QKDZ;t`dh6Sz0WI;Mgh6USEGA!sv$*^)Tf#*3`y7z+0 zJn%dROAqL_bS6kgkEIuMjQ|ULCNlt{d;+N7!^Uv|q{#;=HW4Jo1U;>SWhQtrI)^c+ zSH^5w1NrT#{*|*Rzpz8F$H@@xyFYuuucwOVqjo`bPw5nfsA8;Ppz_`9@D@P z@9XO89~2qy-igj&z@{MR%*?>R>6BQU3hjz9uqg`WgF0@Uj-d07 z!8$!W1W5*z|G*(#%)sUWmX$!r`ef#%=7EnCDrR8w1k1{BLguUe(}Iw-dWqj*WMEK$ zOQPuX21_b&qIZ+oe85JkFmOS~2hhTV%~$*m3j+fi2PlyF(OLsNpg5Q+0h;N;(;5(j zw+2A!IT)B0u`@6TAX)<;CZsh0Vu4x%VA_O%8AO9x17O;MffYo9S_2>&(i#A+d6C>o)q3uk60R{$zCMYLLfPo>7i-AEAv?w_NI-H}(7|3X- z$QZ%s%^=?cRS4EK8_KB>U|{G3>k4ElW>92|U@Bo?kY5Ou?Evi%XXBX5#lWD%#K^#) zoSRsb4VpXyT_ypJ$VAY|PK;X^UvM!nsDMJ3fpLpG69a>45(DElP~$)~gMo27C>f|` zGBEC_1dUp%J>g(r*Z{QxvfNzKwE^>IXM{^b_*~tsEcwkFgSp= z-xJ^D>Hnv>f1KVAv$cz@P~d)H(y2*qk__Ui&H+1B1B`1B3QGE(V5W zLJSNF0t+EiZ!-iK7|OZ9A;nb8z##7c6<;jCz@U=}<;({w2xOi(p&k?}90m+Ji=a}Q z!BP=S91aW$kDy|pS)WT_@gPp-8_>{aQ237|{ShX;04A-=zX;+YkZy3iH9(~l89~tj zI?doX)P!{cUN{!FWPsvQnOih8sP%#7Jea0Fd1_n^r zKLmw6C|VwYqa{BlrJyLkgn{ufIJxS_7o=tyTbRYeiY{=Ol~|Hr#K3rgQ2^@BpP-f< zIGL{FXJ9ZWPlRmuWnjF-2r`|M*#X*@G0Y84O)h3&yaE>g2-@)v%H?TLa{@ro_kdBB zpMk;1nv;RS$P+|_axySzf~2&QI2jm3gc%sLia8k=KvAIG%*nu@Cc?m=J%f{h!4b5* zdpRcqLl0=DHYhdyff@|DM1t`kV>2%_;T#fWU;rnjXCMYBadC<<3e*`~;9u`xvM2K?B4aWD3Z*d=LX<+$4~3iBRRJ#u-8bWFu7e z0?4ofpNnLe$W`Y2Q=}saeyjHx!lBxV8|s;#SDxaA^F1vs>C!NzHlTU*frF}KR(FS z$KM&gpThuD+TvB@>gNL54PppYn3)T@?FLlWfkcf!qGq5Q13{QG#2k9Z45HT!72slk zGAuyno`NuEEu$r5#u!3!J20B9VPs$cVa8=<>5L2vAk4Vj3Un7uK_vr}GV5eyU;tsp zr3|W~pn(i=z9BP^kz7;~4?P=AFPQ=Tc<(8oRTa>Y3D_aF%*ro80~0uhA_n#lThNRY z>jXvy1~x5Fh0DS*4b;&>TId69^v__L&glS}xrHxLaE7jm_64mM69WS` z>Z)i{(8-jDRnZ&4PDHGV2JtyK5UZj=d;t!`s%Q{jf&;NC8pK!NK&*-e@ijQ0%Q>JP z?qCCX*nk7RDjLK!;efA-b_A`825}*)qCs3M5Ers48pO2$aUrXsL0mf!7qTiE#B~61 zA*-T6Tqh31s%Vf+E*#KR(IB^Za6nf@gE#>k&{feOP6P*ZRWyi`zyVzq4dP^QKvzYB zI0YQgRnZ_$1qXChG>Fr{0bLah;&gC8S4D$36F8u&qCuP)9MDzKAkG2~=&EQCX9Wjz zRWyjRfdjHC8pPhg0a+CdVjtjuu8Ib6PH;e1MT0mOIH0SdL7W>L&{feO&I67hLlXw3 zrEDP13l8Y2Xb|TE2Xs|5i1UL7)VyF|R(V5iYC9RbVjfXKn~4CEwRrlVlV1898Kv{zsK3WJ8!#7)lv{HoC5LAxA zPqSq`1m;3U2AO_=$_75jdT0qo&{PbV0}*)42->^D2OeXDh&*8gX=X=SfvgGIcnCUW zmdTa%H8{J1R{1e7xv|~?#Es=lS*9Ge`|J!1;QWoWG#QlZ7&t(KjLnex~`VoV%JOOrwNvTy{! z&y-~m9TM`g1d}%VsEEx{)(pS((Yccq6ai%IZbI_Tx9Oz4v zK@qWl7v^ab2BsP|P+TlvWZ=WHG`Rt^f*iawSqQWy6tOfJ#AV<>EKP=6240#Bitz;? ztHDc?LA(tho)KtiGKhBo!~-u)2JtR{c;KbUAl?HI54gJoD2+L6C@e_Dr2c(}J zsc8aVLJV4i3_1=L(%RU@DF|AF%*h05dTi$eg#-t(g>Y?5J2(x&27^d&kui%48fxqe zqAW~~;up9W7=+P>`2 zpgof(SXKtw5JMgAXYvvUl^F_fNfe#lU`Zto^x=LcAFz=s41Cy!` zXj2KGb#3fZXYnvFNZ^@Dkc3YqfYfs^Fsbk}Fi0S#5Fm`VWgpi>DTCS)oB#DY&HfH?4}1P}*4l>p*^rV>Cj zcq#$HFk%pbFiaSPK@8AT0*D4rC4d;Fe24q`#35bFp01|*sB|w;vsRR%k+N)3ju|55uhw_3rAXh-955k#3v011JNf=?xY1VB>>VA>}bbSN)~i8+-3I;s~GZ?J_0 zAU=F50mOk#C4ktlsRR%kX(|E4MNB0i@!(SlATi{r1m^hog81Uh^!T*A_!0(AgL*~= z2I=@vzi>}K7w{}A0|%q@RelBrnJ~y;y<=Vqq*KBu18RIQ$OIJS=j5k@$`J-eS&$H; z4ERc7|1?M;4(iGkGcd}5q?lyDEBs;AzGGerw28qe50Yk&J-PWsuwFC+V;rdG3F}FN_Mw1=rWog(6l7qK19cx6 z<3PQ~ObG@CdDmj}eF*WOhNqh(1A`o>xMxfN)o;pD3=HxvnZ*T(CCM47MWB{E=DI_kirUNg$4{t*+_Cz zkmU>+l)91RHbCXT?lELgTZkkF4+TS{2nH?fj8BCQkuWeCgTinFRMQ(!%z_7nR|_#P zsDlQDGg3jjSWH2xIGHn`V_piW42)*C85kJkX z$lTE(kmyS}Sb{qON^mXmNGb9t$S_F-SQ0!23K%X$1_q^HPzykj=_(BN5p7i zXUL%BjwBZjm4lk8>I0Pn^-gAmBC*vmy8pKJb_biv*)QXss)B^9@TMBgh~&$T+{NUr10Sg8)k` zlQw86ms?I|UN!?u9GJ(z?gZh+gSi|G930<427yn3f@ow(U;+u@90g&E4+`;#cMkFl z@pN|diHEJradCADb!RYOiINRvW?*0kub*OxmaPL7aqQqb-t&t|Fi;I2ole3G#w}G(4$r^%^JUe(BA-G+_ zz!ERp1zKRl4z9cNK$SIAA^|L+!VYQRuq1-H8rWR}9zkMB0=r0uJ-8sTC>0!!EXiOY zJqGOlX3$_M1v{1w<9sI0R)X^79u@EkpG45%O^j6_uN_hai$E?1V_>WXrN0wu z3=FcM6vzs_HrD|(yF7g-I|G9ph$#pAB9Rn z)C*fexv&c^95@&lq##+M0~DC&)EOA0N*EZy9B~Z>1{pu74saM%Ksjh(B(fH}xB@in zK&cqC3`5cZR2;z55vnwJ4?F{-)NBq=!-fNzO?W{`MFCV+fKoGL@iwCx$f=yn3{W+S zpm@noW`I&``xq6$2dsk$w*8C@cR-huFnkvrO6{B6pg_REVVc_8I&Z@uhq^KLNs|{>jIbt_2stk z+cKGvZp&mwx-F9#`L;}EwA(TvQ;y6Nj)5AY9NnO~Nof0+c_!#WN+PEmS&%N!1UHyj z_J0E{RRA}bSs}M(vVoh^5E00&m^hoc>>!7LR!nh#HZQX?a6$*`UHpS#>+&5~w~KCN zW?4c(_vIi;Edp_k($SAYK6j>j5xN z0Ay@&X)XinK`>v4D>Xi|fPwW8m?r|3WneuF=818o#^ObmR!Dx$r+h>sSK>@Vj%k&xF8Ek{L`TQk75Q^4X_Low>zk* ztSPn_R3LM~CsBecbCdFOLcohuS+&5j90)_elEn=#Vl4>pU?~?qTBc&CJ7)n-7*_20PL}4K$j}z`6k9 zRH&F|US==15jf^#rssjrKVe-4R;<9~mYI`^B)l9ftit7+n3Z4T zUyxdqSdy8S4!L|19uh0Sp`-x`30#U+f)!~&OaeOzu74F+N(Z7JRcbX@N)MtdxHJiL zcu;CCT<03Fv;jmXru15{v=LW$W}dMTR1@TmIo5SxkC{M&8*C{9>w2)D1ym4{ii;Uo zH-Lq0xSTUmle2?C#mz>rhyxegN(RIC;nJBgXP?y=7LWmf-VkVVBG?a z5)WiK=lopIb)T$T!BPQGDUZ~|0=TYiV5tZsDVV11V3`CipZvs>)FQ~J0P7C0do$qq zH>fPpF)sy_X^R8b#EmD>n<_SsdWXgtc96bSa*woG8L?t!m3~! z=tw&bJjxhYPl!$BU|`^Z=6$~qXl@KPGhyIhWW6M2$;kl9?df@mC7=VISTBQ}%K%QP zkSJncy#g-uSh&y&psV2AEWrSCBZ}#)*Tg`}rdc^|gDy;l&dDNFFt9!a+s(_u>L|XN z6U#O?Rwr>#p^381jnx?}1}|e^Lr1V}Zmjm=E4UaKxcord+*lpNLAwbVxWMz?tS;i9 zl>r=(a?TZO4y^8tfe8834NfQ-sw)OYfdNvVIf6B@aJl6dl_wUZq!u}6BxdF@usVaqIiTV10_O2>AzSAP788J_dra$C z-N5dKr70{5+@;oWGcd4nfE*Ci?tXS6hHx33bkVa zXfVY8Auj`iA}H{bKzwlMNx=QDyaK8g?0*K8d?@#m5Cg+cUIqqL$e5hk8>l$5Fhs1F zL6tF(sf0n5F@n(ol+@%w8w)_^Y{^2$FXG)(OQ0#kEx!obXkcJW0y&&vEhNxq!vleZ z9~1}-j7eu085oXh!2%!!6aX6F6qE`|L5x9Q53)e@KLB|!;}9RrgCIV-2Q{H;As*BO z#jq|^NP&fcfiXu09HBvqpa@n134xQ1d}&xifY_91LQkJD77FB$D9{c26w{a78MF7JzIofT{xBx(9MF z0G6_1OGAA<$bSry&USe))F$3cPP)QT89uh^Tz}i5G^^On&gIp#9;{i}w zw$p|A`Vh$1kb>wiD52cZWnj?G%mwX-bV)5vW?($h%gw+bugAau35TPgaDXZ(2A>ia z6z0glcnnm>M(II|sSv2`6F`1E*&zfAA`l-vh$^9KAwi@A%Ef(9A$yqPFNuS4@)bP> z25?^1MOOe0v;=)a>|+>=yopTjW|M}KC*ssNfBrlE0kh00}13M=N2#& zmlT0%HXnx9;Gxo$B12{<5i~TKR$yoVI@V|dBng1#(m(;ZLj*K-npRMhTAW(Kz{sh9 zCeJvB;Vo$Q3( zVPO_;W};heW}*vTePnE;S6q@U@)&EW1As^A!`V5Dbcs%OZ+*wD`l8uomSh+jiP1+ZHc zbaP8e^HOcoQ}a@bGLsc_a}$e_Gi)m?%yi966m-*J$lpjo7lzZoj>ybQ*M-=opqrML zuUiZ{9aa~#s}tP)Vc?<6r?5bTSYf1>OsA0hCW;tV9i$hHq8^dLoC!(|ph%3*%uC5k zPAz6&0ZleBCNYAVhb$2mX7M4Rey;Js&K|BVp+2q*3<$9R$Dj~TN1ym0SNC`q*8o>P z7gs;$NCrlPir`?^cpR#jP*nv7_;`kZObTXTM##YQy7@S|gCygFLmYj47+4T0eEq{B z7+8_`kP}~gJ%fE6L!3RJ8rTpD+(Lt0D_MJ(z(Pp(Y^6-^brQ)HOad7$oiMALh!yhmdv+@Whbj zM@WOh%g5C*%#}d^As7%E>;XMphd~e_3^L!xH6A3(AcPQh@eg%^2@4~HVXFND7(@`F zFvb1>45A3p07sYjU=L8o6TU@23{^JBH6%31FPK3bA?)TK8Wisp>KhR6;^J z^BnKv>KO0l9~AHC;u6FlkB|-a@ehyp^bPfi_w@BOHUh;2g8~j|3kF4m48&MpSKoO5 z06!P7+0G#6Feo9^K%*r%B*@dx-9Laq86oBC8SLoe>F(zW@_U$TJVZ@=K#+etEENSa zs36pXf+N_`!;L`|Rm|BZ#DzhP1zx&<)H#JPsH2K_c!Gn=&C@@CK?7AP7)eSKRm#mR z$kjD~K?@=38W9q2VPI@w4%(9$9OCE~63n2DkcKRn1|2U2NqP)A2sw9GKi42g5f>Z} zD$RleJbio^bP*~*f}wt}Q1S71bcqLz#Q8e;ugWO^e?-Usy5aA!hV1$qaCC8xnP(RNQNKyoq zEn%+C490krxCQz9#)A}@U{&Po?+0C-9?W2hPys2`QIeZm04Ns1T!TV_8O#tWK`8(d z5%I_>%(1F~*cKAZV1bYZXNiDdk5D%!21|r6)XV_KAaLOt?;7Fi916bdzzU%Vk}FI= zyBNXcfoFtkFoQL+e1wsySv-ztw!xzi95e9vvqe#9Xk-DGu|vo}5@CF}XNU(V-!s@F zq#S+19V3I|-5mX#BjbG?Lp(K{`x?fFc{;}X2Qj!KWc>U?;zRuc96f^^oqSvwJPXMY!0hCp;_H%FgfS5Q+5TrfNNdNKqdlt9}m z0YR=Vp3tM)JV7N{FhU6=9Kb1qAp{`|i^@P~V1y#199_IZgF#^rN;eE)2nip@;1Gsz z21xrbu_!qMt$p~Gu?4hO3RHqS2044ggK80I+YrhR0EHN|F^DW=;lu!G`+)@`99>+X zEj}>cDLC9Q0NTI<3xFNM3R<5FTBn?vqL7@CSj2#`fR#yr6?9u)iJ_4N1CszdjIB_b z2hqZSI0u%48FJ}Bg+f|ReqssAK3up|N`5KmXdos9xCr=EMwqYwOME<7H`qP}X3*V% z*?IZpdGYZKtf0fW<3WdOGHY4$f(}x0JuVpJwhPA6W_ zBApBWA-z#9)H_m{xS;Ycpz;jPpp)wvVnl~bmpdgVClxeb3c3&h+D`@@%ns^u-w+$@ zohD`u7Vy?Lre@G}>hSf}pxJDA(n74a2JM2!x1NL*x}uu7j}fdCetad`g@9}>IXU1Z z$dEZ^M&1HO28IH(z+;@lo`JM>kQa3AFk=vVN_kK!WbGgyXpoOFkUa-Hq)}7`I(3~N zbjUtq1Oo%;b_5oN!$T?=LGMg&KnsX7;zK7Dfz~I&Q;|Dpc|HpZxI|$jjSA?kYVL` z!NkD80}6HCNlXk3Ak31)kO@xs4~A00FDuT2r}+cWumGKF0!sEbC9$M=UC@yz%p8b= zw_wQ|bbkvAMw-vQ3>p`QA9t+=$`~}s0buK?kOSg#i{s&e!oVWM1UeiHJ!BZ?fTjo8 ziy0WXK8=naDn{Cy@-i{0fbounHZwfLF4b$ z;zO>KEr0}$UNXZ4Xds9vfsz{o!)+-B2HXSW%xEXCGfS|8Qxj%V1np3RZQWyrrB7!0 zkD#Q8exf>aIjDe&FJWLw3Io--Sj$`{$ntn5r1R8SIY73sa^!$Cp`|+}q_fqTu$`^W zgnqU(ZiXW1e*K;osQ4Gf*E8w7Y9=nH)v;HPJTH9 zQ#Hr}2KJ21^b9D6g@GG%04N7jxf$pf^a=*1idCS}8FY9pQzdBYB2P{R15?dQ&{?0n zAf-&TAoIBYf{JUV`Xs2XhAnVijbKZ7K?<0f96<}M1(@1Tz}?URmWH|k%wgdJbt>ai zD+==SKsVPhb%I>ZA|SxQSCm?8$iUPE;WL2w1`JHyV7>wa4+HlR(6&ORmKRWGwCx2s z1LWeKT+sX@FG!xL7i2s44NxdC^;?5>wB@8QFiixrQW%&fIf8`J7?>uzgBUp}B@9ec zd_hbQYiclvl~%&QG%XTzl>;jW$PQKxP%yD_fV|Dh0kWQz0~CU+9H0>41=+V0^9423pD(4=s4&LH%~nhQSY@ zi!XTM5#lh3kDyeGBvA^J__Ul6#iY_4n9OHzIzyOMTATq|Ur@rp_yts~AR7iPZy3LV zdSyrwa2?-3%bbuTU>d%IDhniW$i07zKhV5a3K~KzVPO0T3Md8<4$#3IkRmreGY@nS zG|2D2)Ir+_SwIPy@$W`91_rSJ$UO-0o_?MoNZer8koa&blhVcp>%)Z!8bMn6VSBrr&V8p6d4jQ)(E?9Cxro|#gT!N3>*=JH4xfL3M& zf;j?`MFpTG6pTS&u83q&L2^bB17k2Fs1THpEXl|(El$i!DUMIhD2fNG3<0Z@L6rhs zoCngOAeo$B3c4~g46I5;vamG2B$0tJ9L&{_Osy!$&CFw9i~#d>ko{J|z!=F`%g(?c zA;9>Uc>%~%h?oMcc}S1XP0TCFEG|xDV0^+1O6Z^)XJNSsq5@v>F+ODmC0`B(X#vLD z%zB&*3^M4ZL59pgf%%XbR0Lrt$U&%h#0)B`WP>=cn3I%QlABn-!1x@yHA&!t=>;<= z&oc0Xa-kjQWG_&`gpwE`9w=d8df?>oXf<(%frB+461n<=Q4rL_k-w~ z3)ZEORGO9s&hkr{YM2-pKzs(ql}w;EnnD2s<0>#q66vR4jW`uR#%pgZ9Kuf6*uquV(jLftW2F6eb5AKvOIG=$roEhenXb20O zR2j3uN)#0e85oPf;wS-`np{@Gz^Kjw(#rS~t)r;GhEyY%sR7gVcaxrkx$+J5?)CIp4t! z3TV|`AU|}lgUUp;L!c|KlNlIKvx9ufps0|WnHLWYeR*(CLIGBe8Zj^`fJGP>_(2UX z(6VCzP|+n0O5~Zvd5L*B3`}<5*k^X+16{ja0pf$at^jHpIx>ONcSQvQlM`5#0;q+W zT2TPwGpHj{B{&#Af#VF;C;I~Fk})vOspV&2(8vWHqs;gT9K**985lGx7#Ke>gTjNU znn5Ek1;hh+N)2@8JSYl3F@vJ5&4__P3mlCQKGQ@6?Q90dA7FEsCZ_15f;gc3_soca zK|8eo#07;R)5Hwzw1P|q#-GffQi+rK0(7UcE~u!uy8$vIasjkR@-MRuKLdlFJ~snH zwlM>PK1k5OlAD3S(3P8k;WkJ}fsKK|Fo2tZfeU<~JcCjCMu-6m)EF3yL5C^8j5lVC zWSW>_3>ohUGG;2*0FC^V7W6)3=CqX3=A63>faO`*YTz>yG+3)3bz>pgEly=+A)Fjf^Kd%e+^2R z_Dt8AKnKjPsn-FQI1XU_S!N6j78{_}DuC={U|;~&PGylQve(VU?mXQyHHt>so$Zp z912kLjbt}Nf(#@JG8!%e$tX&K3=9IGjKYr|Ns!2=W8?h6~HHNa9<;;z3LhHx@H681Zhw z=K5j=lq3Z%f->{c;`58*^AhtI7_YK8K=sXnJGw-Ofx#4RG}ZeU$~2-qu@!E(2j;yaiVM+Y*){ZnJ<)Vw#u$irYJ2t-e+a3`YG>8$kgI3g?Ll z(4weC2pYo7$RP}Nb5UYSW(DMCK?cSOmIqKH-oPErCIWL*1=vv@)(i|*0c;EmMZydW zkQ6u{?7nZ-3=BHp6g!^?L5 zFj!20so*_@|bP{R}LU|*?`hKC=?kuKrE!P1ROKa`izzJ0@SDs z(AWZaWj~u3EFrP7f(j~GTUbJ3Wd#+*Piz?&G{ByN$iKIP#UU#zC}AD8XJD`}+y;pY zM`(y_5n*7^NiNAKW?*y#o2ul%z`$U+7OD*7kxL?wpoeD;OPTEuLqM{xz_O4m6#^9p z6{x(T5M4Fk62%Y{YsCx}43z~XU1z8)Nc=Wb9Hc86EDo_y zc?ZHFm03sA7O|eN@3^$_H>#&;_;IBA~`MurV+&{$hP5#=xLw#>>F)(}96O545Bq+L3`l zPo9^7VK0aw%FDp;)scZgAEe$Ew4#A&Vt`Q})DTe9#h5XO(b$+V5L7FHlKey;Q2om^ z(F2rX)1NTp|YR|R07K)6}Fa_p|ao97#OUevLy@*mW;a~ z0SPMiBf#PiGb2E^CmC@;WkI??!Cbsdj~;yMoo2FudD5Ql=I_9?^zpvg8+(Ps#X zV2~%LLDhhQ;4efC=r}CL4N!4V$~I;U;{MDSVJO3)pu(Y`21-ev86#0^RH#qiupNME zTo3nY4(J@#3I@hEY>3S9h7DA*ef5B4jxTJWk`bOcU|eYC_`(J%Cv`y`t{YI}KY-%* z9~-k21B2cp9tMV5PX-3P>pTn$_d$%)JPZtuUJML+2YDD6n!Okp^g-%v-|;XofHDW) zK1jg8GKUe!OA|NLg8~I5bLc@8g3_U)6exj!k^o22`3#Il7j37P=#%vgGV4~Mg~&M2(lwWQji@KlIOi(At?p! z`@lmI#)XEY6u1Kg4#_^K@e1q=42+uWzS7W;oZt-&Np>Fw20c(n)`1wHklg0Oz@QIO z4-d&BP(uiY632brDIt7Ag*k&G%q&WV_m+vLMsgW#BF}x(^ix#U`khDFL@+ zKy`y1Xpn*70K@}b@cLdzmVrS}6?BfdF9U;~Ee`|3JYNO|Lr)$ChQA<2I1d9uasUH^ zK`IXegJCHT14C~h1A}2B4+Fz>5Tlz1wDliao+(0&4*7?OgId2(`nEw3OLEHQ-CD-Zt#=}q-O2r044eN!3+#KpgwneQf3JQ;{@>eLggV0 z4BAx;j1xIPU1!imaJrzL{W7Sz3>*v$j8i$7yhN^rF3=W|T z42A}L3=9{;K&6*6s4L8H1mYxNkO;_03prds+Ckl67j#KPxA<)oNWCs-%)}aMf&s|# zQyfVO3=Db|ybKI7(F_dwAVJ#}UQictLA~JwUIvDWXa)v@S-cDkhRb*v7(T=>Fj!6C zU|^Ul$G~8?otJ^3JC=cgK^K(Bn2tf5b^v5>0f(AA1B0HEECa*qSOx|?3t0vRlQ;$j zD*;XhhM#f_47UEVAY1zCjr5@UK+Qc+2?Xgp!kT&vmSIqNP`VM62jv8CtIcTEafp4$ z?ISA(P6mb;c?JfKRRy*p zhzZf0yo4k>2`meCfaND7@eN>cWL>sM)}4dN!W)MENV0EWvhapsE|M&Z0^G}%tx$1L zHK+j=58^0c;4o0(FaXtq;6h691i~+F5H*O#tdSv-ENUPySSmqN2B>Ih2kQ*vWNv|; zPYrE(8y$i=0i+1jk^{M!leqy^6@x2czyvnL6%U&`yalRT8Q&-{Ft}mJ-2v6C$T_<_ zxu6s@;aJVYD67c8AYaVDSj_|)1%c$|YVddmvIdB9B_>86sB$GHP%j^%TnVDw9k$8{ zI;p|HcmX{4mH-VL@I4Qp%ryrzV59+^<$yR#RfNnA8P6G9n7#J^s?G#J}l^cm=sVSfn==1U!7%zjxxic6T z%)u@9tKdOp=7|mU9?*Hc;$+Y$!E+8>P;oRB>gWbe1_s6_92rWWwl^mOgW(kg1_tL0 z1_n#^laLf4q|U$)sRT+*L7dEUp)tl_CIgjLQ3sU~RvS1O82r>27_>odPRwCo{KNrj z!ZA<$Q0D+1L;B1Cnm|m;fb`(gK{H-oI6zZ*don?t_{8MwlA^@qYzD@kVE?blVPMb! zk9x)z7l1bC|K$J;6$<4sFjyIIF)%nQLkwbI+{z6~87Tz}46yF>R&G#tnt9^8I_+fe zBqB%=XgmPq8nZ1IApQdPsn0?=&^|TG#2s~Ppr$3LSIshUMI9RhgDxlqX`g~&{*V8nGAVil$#776E|QlKDbU|^6?0qrG+g}4Y0jGCYdp@k#ZC!n!4 zaPbIs4=5v?>qoundH%GEiq=h*ANk15g9e zC=MzPwSx)aS<9JFk1EM9FieJO;iv$W1vLx|mat%ORA*pV2a#o+xTS6i3xnk>sNX@Y z?Q;+*mWc~MQ4+`m(#n1Y5&)plh<8}z-J$ZJehHr{*sUn~=fLD;7#P&R@)2NjZ^Og| z85q35;z1Z;W+{FaVmYXNkPTCW^3-80 z@&QnJkf$DFk>3rK2f2$GGB$)_{;cy5cY~@aX|O!RT{A91#6j*gMu;2TzXTBnsRtMK zs-U(LDAkxvhL##W>I@8)FQ6p=NL?{PzoiW{_CYPzZm>AGm2C7ENgQM@sL$XTAD^C5 zlAp)`qdC&N(m=!73=op_Fr!y_a#2ZsZW05GW?#?j4Y~>yggFDfa+8Wm^2?JMU^M$$ zW^ZX`1_lu3$nb<7KLDk;%)HW5OH%X77@!n)I-^fKGXnz%vvM$bmM0g3MzNq2SG{)_ zGXnz%b9s0|i&`kfBgF8P1+==ZOnOMHt^nHc-19#uH#ek`iI7U|_nz$PK!voq?HS1?b*B&`r0}pp{pk zLYRSp6J#aREk=+WSA2X?Sv>e?%y<_gM+QbUZP2=>bkJ(zOa?|ZknKEK42&9JMmCI5 z#K5QlxKF}R84+|L>_(2gVRm8x+4_X?S0%Cy9QJ-DJ zz#t%U8Iqx|fEHIVf`+ldYo+o~9c2M>lmb*K;-v3tW(Ee~Vg^RA^yMN32H~QzR0c*1 zka&181A}lL1EU4V;oFKC7mIQR-JGdNs@_}>ybA+$;jw49g)vY3bgwuTqB zL>=cUbmDJ11}*+m04=v*=6C=O<(r@t#?&hUV?pNCW2Uw1QP)-M?&I&*Zi=;1>nn(U?TA4NH78LawHfBwj2qW4_c0d%m*(=f(alk zM}i3X#9`nAS5F`( zaPT3jClH?pyqFkNJ%M-ve9-C%#1Y{GS5F(685krOm^nZ;fvr3Uvyy> zKzw{grU57f!6hBj2S$(~%%DZRf|U$R9~eQ-We@@>D9KM^U{uNl9VG@Lgh65qj7r6z zLJ$%Mj7lKQ!bwmWYeohJkxT|g6_BV%HjI(Pz^Do`KqM)Zfl&>lU8DleFo3c|i$KRH zxiBzufE>=u0dj^Itewc1Wy;8ak(n9ixbibFh=bRJWPzAvRSXQ`AWO194q&bW@xUwo zvOtzT1M#yn^FT{cvOrE1ZDe2&hpb`&Yk1Vez#yIn8-f4}Ni{PtNTj4D7nCqCW`p!F zO*D`ISBcr)pv!uO%pwNHNg&rn zEMj1gEH;AjdKQCtrchqcQV_2oH90;xu{aef_+c3XgJeljWqe_2CTK_jY)biZ1_ntV z2FA&nj0_BiL7XH8#wnmw$h;C14~9^l?`lvqq~?GY#)74rK?1os`B3@YU~WbxjJpox zvy4obc9u;H43fc6Nh=U1#fX7%DyRtH+X6DV7|JUG@roH3r-2f(|2B{}44}M?yFdyV z7^jOeGccs?W?+y6$2^$JyN7{6x+t{-viy;8XZAHneI}EZSX>gHmYA87n!><1h0*>F zR6IW~6*7p%z-Z2N4kntH4-qV8U^HSq;glv-lQz<8C3;WkttsWi7BzO;aW@gQU3 zGpJZ{PHJLNd`VGaW=>{ax&Z^@S;i?Zp>oB!i8(p(kc9b|G5ao57@U*OF{M0)@xco? zEx{&FWKxIePE1Zt1?8)5CY|TVD)ZCQ7#R058N#$DXXF=Dr55EEGccZDDu5~lUvo4W zTDup38krkF!{>6Kb1AL%GBC&qvoJ7#l9n7h3j;&dUIqqvkSYaT76yjbdl?uM%vcy0 zrtV{4P_SoVU^oL}c(5=qgzRTvPzYjSV3>W7fk7dfg@Iw;K?VlJR2BvXBWTboon>ZV zP=j(n_pXDRhR*t&3=GOIKr<>T>!Hr-=Lbotg4A!h2k{~3a#)ZcqXHA?c3(M=hRj0@ z3=C@aP!nhKGceR~GBBt!LIx!mRA6zwoS%U~Z3WmSLh74*qkd!`zDb>VOYRsU28tcT4`Y9|7YA>NmeuI`5LX~Ka!hoR+%1Nj^ zICu|0vogr4T~NjKAm^}_Fo4{m@*AoE6q;()4eVazKi&pehEZrbehdNbxsh zdALWOAgPexg#-rJI=G5|P!%Arf|k3eGX_E83N*~9mIp1sKw%#M(GQ73wN50N42TR^ zN7)mI2SEw26Dkf4e%6T}zzMe#s=!=;fng=G0+xv{Kv`raR32m{XmSf&h=67GLS;Zk zKY{8j1%)guPuztT_@MA$1D#O}UZM#~nXtt19Z7`-R0Y@wczCcv3ujPxxFM?mt?*P6 zgUW(pBn2v42XX>9a=oFlprqUgm4%pY$e}*Bh%5;SP?c*?c~FU~_6f>y7GPjF z165oipw1Y`R4c%s_8%$>vgajC7UT+0nUM;un?RCm{K!#m$e>mVl>|9L4OtT62#sq{ zc~A&~QUW;UU}3|+%mM1VFmr(Fe{N9wAcmiTL6bo%K0YZky(BfS7}6ZqU@GEgV9*Za zX8=hdi+91qbr?Y{aux;#CT%7i&_DzTGx;;=fVu!6%%sDlbDWuh0fd=cnRNCtGcbTK zlL?c~CuRl)5N7gX(lKFSU;tqzPbM8L76t|oX0l__k!E3F0AVIKCLPdqA0W)+$fWax znSlX>nOvB3<}oucfH0FOla3Dy0|N*%xijg2HXVX6lO>Z5Xk#G=GdVNq=BDN*7gRDp zDTYdB(2#<*{7@QFfR3d$ynKj<2ltJ<_3OQn4Llo>XD3L30Lx?>2(&Rdog&@LFJ ztBgNEV-2tlDu~0t3htnShPXIb5gk+zpNAD?{FD{iK?O;Pu!1|N{~19YR5p$|pz%}C zNhJKBzC5T;#lXM`vXbc*BS?-bK0YTOse}3gJYLJ}3mQMIWMF#12x9So+L%nQz)}os z93bO($1`WYH#t4u_jv$k? zk25d`gN7`RK#hbhJYit;6ft9BV3>29fk6~qP7HKJ#S!RIzQv-T%$J}rgfbsA?_Gd; zIfWY%jGzO(krFg?91nI+8H@5e(BK;tM)g1gddNHdSvVl)w&;TdUgjAb5)ZkTmpy?AB!o65 z#*X(kUiK*2K+tVk;M;iFqhOEGX@zm1nY1}x3Qfqok=d#vnxkRcoxxAC&a$(n<< z&2xZ{!h&2iz#cC<4|F3f2l{Qi>f89@rU zeLyUb7z0lT3j@Py(1p986MYyN_&`P|FkfZl0Nn}(u8WwjfjJBu*$m9r89_Oag9BO@ zLHRr!;JOIP6W{>VMNp0iM+F1(4MsjzP+i2r0kR2fr5q~*124!*1_pkRmCUyoL2?4| zo_QsyMLGG&*{Lb670DTidFiPPO#hfc8H+iKm4QJJdc6VDKL{6cYZX)l0}Dq1=zd?2 zYGKf{RXoTf21XCYm8_8IA`g)JMa9ZMEjoXYGEwnN2F5^;C<6lnLQgzs7$y%iG-Jnj zi_-1taJ_U%^ZUMoVyDOC~WeT7xAR1mfe9a|1hEN%%Vun-f3=9gOorw(1kntV{RV#$-3z)11OjgYeDw`?_)wlpEz`($uo&#ko zFb0W#W(Y59HAcP*a$S88&l3-3xOGgZd+=EZF~W zA2Kj7XvfDpAx*YdfzsUvsA3U#({C{+1A|UB17j5^mA0K^V9>2#V5|Xgo`N`_iR@aC zPU%w&47#8tA&m8)_WjpW3=Ddp{3i1j5|a*~JB=2mfsQbE$I8IqeVTzm-V!PYb{gEp z4EhXEaZt*tf4vj~gET1T3cQ0@nhXzj zV=e{;86GwUhUU`@46+k=7#LJxB35UhPBn$w%s-^`M z2RRj#oDCQl45FZ7ouH{x?fCfI(wvgSloUvIe*y}u2B?aKaQ8`bgI4!3FoLCDonc_m z$z@=C0t)8cXBilDK!-(uIA6{(Fz9A7Fg^ou!p<=;=z@x92)7r+tzck$0V*qAgE-Ln z?w6pJjnjDs2Hi>qMlh!v#7Qz_VEh1b&ARgp42JKaP68!ReS!Co@B?kgV&w)$08^QO z0%IUksQ`nTG*lK8(h88{wip9-WDDBJ z5uoM>RR*#P6ljJF491XPf`pMPxRgwQssh~;12L?hhk?N)k%18+A9jI(!6XUB+j4<{ z!8DnH(H*Q;=pqAyX*Q_700oo>h*zEn-Jr3elFfe+8i#pXy3=F2A zA{fHWgK$$}(o4YnBpCNNgbSB`1?DHiqfi+Kt z%E9kwJIc5Orr8dpc`Z}{x@J&9+ys+@*?fTU8!rQc1t=vJGAW}=tAG+_(hUX%wLMUc zpzwtzO$Pl(&;SJ`W)VJUc3=Q!2L=YiuTWV~cF^aC>Ianv77(S3h79`dP+?FplnWLP zlqzO0mV#}wGE!lNg`Cn976s60i46LgP)#6fyTO`3gQQSv)!L!5Ae*2r=pxqX&zj15{L^*Tpz&J-UV?h7#Kf-^OnzTP!|Di9CUFiE<@z+h+xbu}n*)UH7}pkP;k+#$yx4Qg>Ie1`bV1s3;nSOh=^-moz+ zthvL$Ad|_)!0_u11A|I28v}#!T?Pgf(DLO>5TlEYfuRS)n99b$a2>>02x{rwV_;BO z&Bnm6;vNIIq)N$5OM_%MOK@wg0BR%XtYMI)jB_dlAte<={@XoB*g|;Q?n6o{Yp`Cw z2avF}0k_(wgLt5l3c@`H<|Z*PLbxmsK>?P?z-S9@4{AWTsW5&Vn4bjWHbS^?=_O!( zGEDk7hznaIWzPgEZ#o_^Fql>{FhY12A2Bdkf&#Px>OBQs1_nlVrU*f3AhJJ(2J6zt z3=CQzS#XeMK7j`5CJ+M@q#92d7*s$(IupbI1*zyWXpo9M2L+hnRj3uDQ*fXVB+Ms)!?oi%1A_@@qMONsk%8gbb7;s-0f$`T z3ozG!ff2%wf62fgSq|cX(i$iTrZ9n6YhOarAcXe~#LH%2oC=PrnpX@ArYQ`J)0m1_ z7#QxnVqmZWO*f}7Fdk;|{|fPe^&U0`1|}#6bfG24vy2OvG_Qjy!YHu6Qed5ilVEo< ze1mA^g!^lcFav{0N(uwxNwCL-UPC-~66~=BAYKx9sRl$q;|-`dXJ9-HHf;llm%_k! z2JFaZAYLW|<2kSg^xuL!kjlV#0nF_IakF8(Lm*xS1LH+-c=5f1B#evTQ1Jlqsz7p} zFq-&|fx!~giwK3fJOPwUzAq242uo6@@7*3_OpaqGyprc^Ly4 zEf{zzen1K-24;i?uoN#NXp=cFV*V3i4l3G6Q373o8S|c@P&o+>reJaJvUm=9D9EWqkOLB&+hz{?oPR0>Kp z3QRR1N&_4^eDTo000;JACwZ9T<4jOE{Q_}7Au$USqHaGK7`Th$p=AaG;~YrXB|yUtl6K~T z{L3`akFOJI57_Z7qlg6*ck6rA6Q( zfF;wPk%2)Hy1@xbvGhqbvoJ7#Fe@j+d(fl+188p@2P5dRPiIR@_=TRJi#rM3>Iu5b zv$!P1Ha)M@(vsr4I>9$GfbT>tGto29Gl5*u02W9tD$xg5LC|euddUn2pdls%-Sf2H zctEQlP|3u?0m?{&pbBE)xCffh1E(}b@XjaLeM#)`@z5Xy*G57h2g5=Vv~2{GJsIbK z?oQwUmnuS_vTf2&1_lmL-6RAuk>eMv&H*Xw1My(_T^J}8sm!woq`QyIif`^~_>BljCpPF#l6Frbt8K(&Ygs1}*X0k1_s zhv$RJNze&>yiDZ=j0U{mbNX@^`1GMBTm#RWvw^A&eyEojnAd}IxC2xPqVL@ensWqu z*Z}0=&ficE8-P4~2*d+ras!aE-@h3c1Wtfz0VD9nnu!72CAkIhV9mxL%_e^s82B=w zRy+r7%&CHMJ|WixY$Xg_|Nl!tN+$*e7I0N31WHB=nHd;_K@}Ve1Aloks0zqUDrR8b z0CxKXs9B(n0VJ}vfllfMTW^!Qm4Q3LoZ)8|nq;{(;0QasWgym4V7N(84$dzBN!^ zfrE>afNB;FkcFZ+i##!CSqG(9`oux!iGwgJCxaRXsMYPQHgsCu`K2WVr6uu@egB{< z6#ha26S~@i!NL?#@Zr8nf%q*>=!+pJFVC1c5W8$pizAfAHVb%J1B(PB_{tFIvIdqu z#u89x0CAxJYHORdBr)CDA9RAblVh+e1B(%8QPWg5&^$LnINmqZC&V+v19U5)D=62q zhdBnh`i1&J)Uz=#O<-YQU<(V0cl8YjiG(oyLVbJ~7(s%bMhr|~+Ju1_LCx1WEw9|JFlFemjzz5=Z8Zrog=ul5X20;kJh(QR#Fkuh| zF+438L_lgo0S&AQtF8Sq2FZ%Ol7&IMgSEK@!9X2nk}40x^Oe z-CP}mf*d0mq(NL~2wMik4h?nwY zWab2@QiPd`IiM|3&}6^~Qo{+4VI^o=085(U1H~36GY1bO3ScU+gsKK8&^VbHpemuk zI!cij0*r!;svN@H%p8J@LhJ&dI~i3NxVi1PnK>93n8g^FIjp#?xk01vEc^`IUq6{E43=D!G1_OhTFe5kI7GZHD zjtIgg1_n`({R|9Z;*1D~i-W{KPL=@K3$j&`1?)3VVUV9e0#X_b%p5Y@pk??78L+Qq zz%r~146-bY%zU7Lf$5PGV_@b3aX=oB*JHGo1cxCj$k__qAX61s;UUJrplE``Q4(fg z=CF}uU{Ka$K-I^-7$uzB1d^FVRtATixZ5@f8J3EW~PaRvtU&L&1iK}JJx zcxrStF)}boOmvc9V9CE9tSH9ma5ILAgRId-Si``eC(CFk9K;wZ z9>E<6_M1K{gP}0UdIJ!Hfx(cQ5#&gaMkAFY$85k^0p!Qioe8|9H%>@ZiZcvxd2Ac<=;bjZ4b*hsjB#i8k z_zVp8NUoJ&U~oWi?Il4mykz~BVBgVI?I95F7)Dwre~7+m!jKuYsC!12Y(z~BZI z;De+O1_pPi07M07*%leO5tI)=NsSG}U|?WJPs1GGl*-M(z=qiZd|qBjQv55}J^p6@)|%IQa=dBp4Wk;kJVF1}L`SX;}oK$WWMp zK@?$yy(Gv^F;I4e6bs^zVnPC02`F+Tp#cjIX(^}x#UahWz=jlz;3Uru4n}SU1`Z?{ zZ~$|HWk97K+^gK`k`RwVa~U_n=^(%HAaTVR7sxZQMP|{>z5J3qS(Cw1&6o_6IAbAe#Z5FWSxEUB&kz~N$W&_KB2E5VB zMh@h%krUZ5AkT9_N;qy%4FXEhJfJMb0Cx~5+|V5bQ_2FaZs2tiE28WN74V=c3REOh zR7HX7J|5)yidPt^HsKQo*ATD@j~`UIFfa%p9KyhW92bz{191;1m+C^8pq7drgbA&A_2KD|6;|g% z{cZqt538pzC?gmmam5)Jj6lv|U@(T{evlO=$nIibFomk;_7sLx;%4Z1)*LDTZSCZ7 zFfdp^g&_Hwm4U$$DgaT#%D`ZS5CDz-klrnX6}D`U!j>Jiu;oA`T2Pe?O8!VyG8ZVT zFfed~^}zdcJfKPx)NABL(#*=hz=xb!_+gnv0G3$2*`8>22td! zh1|A8%0A$%AOX%k+zbrJ-9&IYfpimD85pF&?uTbB8K`y8tR)Li5lFo)Xqu2ituy2i z=@(S+Dypkhl9qlgfKWqe^o zu7q|`7#Kv6LR<_If}ju=hcKr)Nir}HTXBn{R@~wY4AKx&AaM(7q(QsPvPfkDICaWF z$|`w~OBooD6O04{gCaD3;F%p!t${0EL=ZqCm>E%*k)sk@g$|8haAbqqZVU|IY7!KY zsBJe`e&&GXXHL}Qzy*z0XmS9x;y{@J%H;toh8H1tDo$R=_y#z|34qE1P`M*hQAQLB(4qp8&Qy_7G;(PHscbb6sRiVE zO=LS57(j&uC^|r86Qs%n4}XBTpo$Zu7?eCf<2nosx{&O|z@UdP6H=$?qoi2uB??Fh z#Bihv7F>5R2(Sq<3NbQsNOP!i2%~gsr8&SU9+AMAk;-{+af9gCBBxnIV-C{7<$xtq zPFOPKg8G3KR4;=<29b3@K1R0|REjY$;3))AQzFO+@bCx&13My}4XicD;e)3o49?=< zYzZ%$VMRK$Z05k>S5P^M)N}_WX)aiS$iTpjluUUbX_FU}CTt}0@Kr>lxEk&*%poe| zSP*Am;DERb+W6x{avB%JY1}AILrIgMxJ6GM$gTvJmylitqRhk}pb$Gbp{9V#I4o12%LhZ|H%Vr_GQhQVRx3BgB0z_x(~ z=HYQK0!jIzER4v*=3=ncl{j1+oEs&et_E`?QHy>lL=g^hl{9io4%YSpdtDY(yfZM! zp++a9aECRuzzSjYCYYlLY78hr+Y8_X0MVfgtq#D7R1oqISx|2SQ~-iy)wmf!!K;o? z}YVpy9nm`Flq0c^mfr!WJ9E!b5O3=DP{6{kJOAD~$n&>%cCXhH6G zgcuIZa!w#s3=GalH4HcuTwryKE7V`$#y2Fc+@K){Zk4#BrVbRpdq5`@SQ!{Rq4^ln zmf~h$@Iny)?M8+QzzZQC6aj)+ACx~pWhQFfAPmc}U|)fTG(q(~wAI57tBnwiCTJ!G zCqpi{*N{BV4NYrc&m#?dFfj09L;)Wta6w_t4~`~r1_sbDGkAK5fdOT7AJrz5Komw; z2r^0p5%?gkC@A8fy>HN!QS6V9~Y)-6)}!?G+m{$W`b%;80jR8Y9Vi)}vCC`Ib-f^wYzvOc85z`!7g z$es)gLI@71<`G5;dTs^=5m4NKOoNVIGcbt3B9w$cg9Qt?iGx0%56^<&5QSwyXtv>0 z1JA&6q2@$T#}FFJ3=BN50+JV8;czoB@S)~J^sI+E{SHb|sF@EknjnnGgOD0U1QFWc z!F+H~K<0ZvMY=d59MMJvAlhL$3>>FYP{WZDfHX$Q4Cy6`Gcd@4vJ%Lha-i6N44*QB za-RZHPEZ8J3%1+^t0YxG3P4c_8ZcrMWSs0I0WuSwwczUtz~dSkAge$%vnGfEN*P)p zS1>SW<49H@vvd$a1hNRUo&Xe0&_V@sMFFIohxAMhAdL=am2U{)f%3Hx*xjI8NZ~ev zgAFAsm_Tg?wNu0q1F2B188mJ{TnPpSlobaccUyq`C<7Wg1*LvyO$APlR>*~&HAog@ zZ3K5DD+7ZK)Krl7Bp4WM!O4%6fx!+c4laX0A!m;$Ye18T3=9qk0dS|5oz2QMGc`!Uckg1VOz+NK8Ah_Jd1T*#r%TtlJ>6i~cK362jDMf~RATq*#` ztXL*P@x&actpIBIL0yHkCIFO#prdY}bRZ5%$RKCnTonLsxxrQiz#D4hlvXIA4DlN~ z&cNnC3SDsq22O};85m$=2T=bZhpjjR0}tV_#S?Y{(AGKBY|wNhwBaTMD-?ynsS;F@ zgR(Cu@Q^|nG9V*{NR6OQ9;jsoS>8fq;DJha#Bc{VO(HKG0H-fRiwk{B2C|5Sm4N}) zqXj2H@T>u-Smg$d$$%JGmXM)_45XOlheiX~83K@^Tu=|XZV0rn3Uob=5V8O~%YfId zfa_(=I! zXegLb5Zt+xg~)>oWI4=~2Z~LU*?Xi~9wes#idb|HA=agVTqVK4poDNFWQdr&)rOKW-7Angjox=k>-sDuHF4W-3J`nXNc%uCKG zO-a=+C{D?a&&*3E+XQSHspvVVI~f>~i%L>*a`ciJ7#SEc^Gb>`^K>(F3v%=_7?>Cs z$}{uwO7t>7Td_gvQqzhdoZQU3^31%H)HDb;DX}=Uq&Nk_NzTs&@e>O&A^h@^%7Rp= zKG>dLy$l8x28M#7{N&VPs8n%DN={}HL<<80CliAJWZgTn9b@!-Yui0mrwnz=lA|Z@ z)=f9K&ceiekbNEtBlG?WJuo|sg^~GVMFa~Ib3gkh7Dndx6-_Z5O3Xs@BAIvBbEGjJ zVX$G|QxB3q&!We|$h@Wc6AKgbE_N>#M&^qZddw9ilc%yUF|TJYVPRxGSrXISocL^QYrvsDvC&5-x|xNExdUv~FR&BZ*g;PCS78&$?8YI) z!o<8AEP1Ko)0GV7s(H*@PdN0M?LRFn)oRseU}9lp(_>rAED-T?(#b~KiH=qo%n7?9 z?HZyYeNO7y8bormMMi$swaqwrl0%v~lH;0psdc)UCx>|yhmj`}D+41V=txv1h6qq5 z7&@7PUP(H!BtH*yqy{8WqohYrnueuECI$w0qGJLl{?wwpL{Q2H9cKVaf^(orP?0gh zagRro!TjCDkx|E@qQdMV!%IQPmbo;InS0j@@6X^^v1DXmU}69ro5DDRVuc|+IXO2m zGmmg^A<6)>umfe-3$V}3W6r3G;*QJ$iM(ER2dl<@)ApmOA?Dp z^pZhEHc}A_O2&{-{Rrxx4XGqq9A8{oP>>Jy70AEPiinAU0lDsBVqgH(HITXoOPZW8 z7n*~O7#IJY9L20Kk2&SUy13@%j4%Uc&J)a)9D2_7>q<*Gjxq~!M9gzOS^C-9HiKE1 z$&8Vir-ZpwuX;Xn`8;N>lC^8oPjc8Yr$(%05@29t1l=3S!~i-&Y)EDFlAPk=(gJ8c zFDgla)aL}k6xId+pN_!ER4^YJqXG32%LQU_QlA!pu2=xqKJ%wE75Uxsp`o(re7+9J`qNm^g$uB$!3!G3Rl3 zEr?{kRmYLWyo~Xh?Of(tb-VJI#nRF_nwhyK_%e%fq%rgC@?lowuwhP`=g7>d$6W4Z z&dg`STw&wFtjKYK*^lD{^ZmLf`>L5sIrKOTzHoFh|72iNVdQYSM94CsKnTsQ$nFV(-Z>{4vQB>;5+|Rg+3Dg5q04F2R@tZ@c{6p+G zE`sDB5x_>v;Y-Y3eA-d85sSU%*A#uom|Rn zugAQx{sglL$F<#_wx-MuHp~YYA`DBJ`9b0nm?f_5UK(A>9P{ZC^JRtz=8TAEk<6lc z%&8H(J#90dGmGdkyGAhEL@@hBJY~)a@`|9U?XBOv(*qu9>S>Va;3{b*h&gHOS*5R;u#>B3PiQ5zLbFnA1v_ zv=|r}JHS<6)4)UtLrG3?ZZfPv$1n-(mYxA}3vz{`3p#=l65kB&;DYVfz_^P1#z8@9 zacVNC1HcFw&>^*9R}32WNrxm}P{np(H8jDBGct2Bmg#nAZe%XhlVIj7;ph-Ao9Ov% zcfCh~{<#uo=8!eax+ToPHq5pq%mr(h1#Fm$Ic$8(CVFxxdBkv3oM+xs?**>msz3uU zHq7T3n%**ddof#hF?*!hhjV~TVm66zVD|mQY;1Fed0o91^9_a)<`wmN%y$_|n5#H! z{3mW^o>T9|{Dz@~xx$8dPQ4BDABHC8{7=5+;T%&kqL{0^JVKZSY?zcJnE%yzF}E?6 zF#oF4WA0#V;?Pa!P-Fg7=fymgv4pu&kNH!b4fAZqCg#dd%nkKk%uS45%>U}r?8BLq z#CF$vF#l(WFbQwwaAv+&=f%95v4pwYhWTEd4fAovCg$W%zGdmm&+5D;dUE)I6i;Q0 zV3x6AF8svI^~syL$_reUmavsaRxxofFfz^sC-8x4^g0KH#HSS)8bH%E11FQh256je zGD?D5Kpm&22O0@RaU+)eM@kDAVlV>($iI3S zAb&zcj36EJ^rRf!{ml5OHp!j9rWNLu= zUlQVb&}z*A88rZRXs`{25!w9$C#d+cw1T3{ypl9X%p-Tez~)104~WtN$cZWQs9ZI+{BU$ zNWdZ|LtKFmG7vnC1uCJEGZKp+TGK(sLnfSZ@{<#DQX#^~Z6V}_d~rz%yqp6?=LJ}F zeqv{2=JH}Lvtj14VPRrkP;Fz|Qp&=}{FB9tg^9VRnnQ?%k@+plu5eHTfti;hf`N&J zg$+V(0I`{LgjtxF|5r_5VPt**)-oObMKEuv=6Dj}05XBCiLITP zGvZV_bH%64>CA5!o-lJ=V=kM=%oV|0rpL@>vyPdQLywtj4YODYSiKj=8n#;>nK?fl zKgAsJ#FV+x%Q>g{ICCn80&`D2hY)C>v&4&;Cykl&6SIB^bF?0_YRQXt%weCvN|}}Q zn7K=?A3w#syS|B8`r7B?%(73Ixlb_fWaUU>=J(QRF#_2F^%t``TWR{$l0J8g+TR{Rx+()n`zNWd?L2a>+PZb=u+L=Ewa40M`XXakR zT;-KB1*}4-1mqKLJ?2t9W^S8wI~_X)CKe{PCblvTZ;%2ujtOinR?J*bud+2eYC5ic zdi?CBY0R8Wr5q;A+)bXLV1>ItK@C#I!UpLZPXL*e##YLq$ULu}V+}Lc1ZIg6=5jq| z&JyO63>*{At?gjuhfGx<>1{j((#E{Je$5?bo?Q?}>Tpb0+5xiUI*2&QyuY49k6D;Q z;remr%^>D6hEL3D916^$X&`y#ne}V-GM{H)GG#1fz6Ba4;z?r$#Xtn}p_&Pgy_t_P zGbuAN@2-hpKFVyv%sGKW^7y%l%qb_Bxi}P<1)of0?ylsBV6Nn_vGvSluC$rYVaF`u z#WBevidjdGnR`OZH0J&FHq6HuY-Vz-W!_iMaSg;~F6EfV%xz=KTw=p4py$1xBfatT z)H06jDJ{&LP186Ok8_A+G4qwg-jBb)TyX+4IKHx;!;9Ja(-h_xbrYCpF>;(Z!yLz< z$6?H|yOG(QqlDSFiTMv}Q;Cxwb6FZQmyLx3la{dOZD$UNN)AbDM{`>vix>+BX3mI? z^kU|i2M`@%Fc*3;^GDcPGZ&VCx;&g| z6Rnslyb?I{ZLL9F22L9g$)`7+V=6~!BQwv5B^)9zU6?Dpf;iNCI24&VK@ONVjkzF# zS-{4dL!Kj(nVUo5bE9o|6!ZStYs?FoIMSH2y_iKyn7KGUS=hERbEakAXWm=SA;jU# zEWe9cBqD&hDuS6ajhQ=wIbt5D0C0Q4Y{;R<;lv!Zi&_0dV=G6IHM7_n<^!A@6Lx{7 zx{E>6<)?W-CDhj9d7ie-ER4+OcsL?B)St32F*g>KFmqjFVPyWt8^Ljq`BCi}=50(i z%%^JSF$+v!=Hswou3Y2XJdK&xhS`rpi1{c(6SI~LbC?aYRta-{TJ-`QJ2Pfa4uvU^ zfsq`N%$y}tt+kki)^M0HZ)Kdoe5dvr^Iryzrsk>c<;*rumYieeoOhB#{ABv^v&?rZ zY?v3bM=;;1m~fqWF*}FCMCMBsCDWKWBbc|baVRizPhb}1&||K!vGru;w23u$Ob?&W zagMplirS5tNa9W6ZDnjE_v+>`x7R({ zJk6S!&!(9rD|az(WxH0&ys?VIhMDIY^R>bz4t?g8d}++r z3vHN}@N+bQ({ltHGvBqA#{QN_<|T|Jer`ESa+ta1F>kNsC}F-_IDvUBpAGZnLYvRb zF9bmaw^s&pQJQ~q9`oXQ4uy2)oi%C9@0s9ds!kc8ndV?IC6f6e(}dmW%q#1!9b)Ed$^b`%8#7lEvmcWRBbeb2W6ZDP z*u~7X#yi}*v6=Z06UPKMX3i$&^GqDOnE56!^Uh9P^l;R&gjaGoNQkn|PL4j$;Dz6^2h7K2w-E(_)zU)-YeG;#kAHt;mb{6mJRh zwxW_!=2Jo(6P_|}t>;iMXV&BJTGz~^BYcwiYV9ZHC$$_3pcrC)$;6?+EcPUWd4KI1 z=K0JV3Qr@Mmolzl-jVZ!`8gkl5LnR}rcbXLnYq(I1>^C&2^3jhk@+?Ayx6tOE9-VKFRkOSVRrw-e3j`*BeQTCb6y(rT*e6I z{3pzEHp~r-O^vpZ;D#e)`NwSfH=V(4CI*HS&=Km8egIe`Gq*qw+~WoFAWTpj>BA-H zn1dQ)%pQHqic(|mOy;5zWg)(sN|aw_z4c%e40KwAXI2-Kbd!DvLnj{1QZf>vB+|fYA-aEd>#_AOhUn z0=E&sTrlAYl3*5XiUKW0U}WPs(ZuF$9p(3wInayQz(yA&8wetNKtu+J@Bk6%AOdWF zHHgJ5S`yvd3}UfyM5KXC@bw0b_^5-f;$#@OSb+pSxPc62;))fAtI$|^&A`YpkJ)4b zvyTn4iA^&zKgS8?Uv)Mo^{o<`y*(qXy%!j{`B^yVGbxFzK4|R?S|G&?UNKcOf%!dy z4U>xa$(Cjg&vfQRENRT=syWtxI7dJPv*-zqNnw%9k`eyQ$r0WSQOx2Km@~bY#jkNJ z&ga;u&td1?=$)w_9^O1LwOOCzvu$I!U4#CGy{nixn{HlT#<8-MS%|}i!&lpPCC8=( z91E^nr|WN=#H@OaIniq^vmB`D#>nQy#-ZU8wmUqML!X(8LoX(23WuGy$Lg$|-XWRp z{);&#F{ub}C~>qhmwI_Jb9*tDczJuL`lK@-1GNg~Gp%XuZ?x6dUu(7DrSimu#`6xrg=kQ1lL+{ni3z$tfA~+<$5kGTY|J-nnd}%lnJb$z znMIlO7#A?7a9j)TWUg8RYS`&9PhsTH3vXsVQrpzBnz`(Rej`X9bKwLITjsBfPkfl4 zFf=izTw|8?^4`1&WCN3`6o>*@$;h?_w2lW-e@$Rpz^uU`#LWGqnM34@wY6PiX5?yH z=2i7g&EJx;B2#T6Jt2W|jg9#X1IHTXPjx3ate6G$nDf^#ch;3K_cNW~@L3(%yr208 z1Cs_L$A))pmK>4H8|vmUUuU%04RRLS1U6^vcpFoadUeC0PZ4Ic0z{XtA#F6vy9CKw_F7rZ0P-HQ( zaYV4q=MeW!^|A&pQ<) zuz-0{-4pNQ%#jls%a|J&IfN#9+H!a^f2`y1@&n1U&HKa#Dprp$uHlfhwQjZom0nED zyK5tMzi6%Z1!WRf=I0C?pP0`uUSlp^!@Q`*i}@8ZcmfbqyfHDWb0{#st6szWnYqc% z3RD0xF^6#|FmJ7y$9#|3rWxe2Ni8YNZFNn|XBgLTh{W2aGW#}h7%>+z>4H)uBlBYh zh|l%d7BL5}fw-IbXiXX$haMaACngR(=DD>THcadcjEs<>umLP8iLAgHUO|gWCB_H` zAJ0f1eJgKogVab}ef`}NJ#BSMQ#kxMa!X4Scdxd!=4dTr&gPiMEWu$T?&%GxdYIWb zKq1Tm>i4m*fkarW<5`%P=P`p8q290YvM>dYb}NBvjxzcW1cB=bvIl~wu*#VM5fK=x zkHLr6dz!ZU}0oF zRIw&@700A7W^)dq64P>!CbkGR<^+y3X2lZb_!50)Weyu=fe9Sft!inO-X&n#u$QNM=y1Vf3pC-aVa8|JeNddwBO znD^ALVLro9!n~zEg84rKhYd4t6LYZ*GoKA84>R5bSq-iRzy&X)VCVPpZcebZ_ReH3 zov;?Prn&hTvo42TgyH-s+p@3BeAk$Znwa_0K$WVF&)P^(V~Lpqq@Ou;iifX9EO?lM zNm(3ZHir_31l2%{AQcb-+|KfeWG?e!wgHu7%xoOsEHIBP(vEpY{S)T13{ToMW2f0# z`!Gv!2>CFFH!&;NM1tJ1o>{DfId8&z=I+|2iOlDjK5?X5+kyrTH1(JZCakqiH(*Yf zFrRr@?XHu|*BLn!=3CnulrsNkC@Jq<$E?Glx64t}*~;F^HWD-t1ulx2+4MndYoADK z+e}?hCS?8$64)8->67Yh5D9M6fum?5$SYvCgS`T74}u$%rBSKg%=|XC){&858IDAd zN^o@q*6q!l;?>Bk!XYFcxX{zvry}h0BK@1pnN5M9(Sd@r4DUS^eb$~C1?rObyJN|@azFqf1tpI}I<@eE|HiU`nTVP?Ko*Tlleyq>Yi&ci^TqtqJY za+bBsavVbInUlcvH7JZ2!T$Bu=ScCH5318SA~`G@O>K1}nX7g&%X2(o<}G0^j$r0p z6EkHAb5%)|_g5BX=EgcuGjJl4UJ(m3^T%3HitlBLU^ca34zO`$HuYlW)JtJuX5L>5 zT26nEDFV#4VPRxG%w)sN>BYj#yry;+XawRvV?^XhYkg}MX3G=I?l#PpHU{%SBRZhP zFu4)T9~jm!+H!b%W`b7;Zv?L>`9%L-52%bHyO3mHfZimeTsk zzRXo6an?-wB6q&c%5uwYF0%qPE*aU<*d|A9()BK7uC#GtuJY1n=Hxgrk(qxDb2Nw0 zbY|f-`qq&gu@jlAY#d$nnfaKs8C%K zPzfrE7?}kkn9Cv_wJ<4*f^t2Z7hB8Pn5?pfD4)3;lQiZd=A&=@rGC!KBK_tfa?W#gPUMcoy(d zmUZ-RQ&5y!@HQz(nT;nf$pqW~gk>s=$VpL@D@q{A1fvB5x#a{h#{_GZu%Im$WIay39|vC2y+UkRq=wIk%f_YRz*`Qs4UF|wI0Pzd|9C5QyR`J{E3B`IT%zR z8=UxZqtuf*G)+X1S@M&JAh@XwYTgWGEW*IR@c;k+|BMWb6$(7;919pBzA=GN3=GhX zV!lwmBcDJslMA0gH5Z?N<6%AyM{Z9D11diSN(VsoS%W9*85o@S1bUgA_#}Fm9r+a6 zSRDB@T3H?W3|iP6`7D~*UHB513c2_!91rsuI3D5Ca6HPV;CPHr!s!g3fYVt%4sULz zRd}QwxtSX9NV{?~F!(^-H9-i{<*ZfXINZ3i_`vtw;0xab zkQ<=;dX_`=L&MjBsSh5&0muO?!@$4*(zgMsPZFxnh0h=aWFs#7K>98)GBCjQ5t$ zFff4BEr6=agsMXc$5fboQjxAh4;v1M57!;uD!DSr- z0|QJPwp#&N9Gf{HfBs-*V1O;YPlVcw?EU}-28I|01_qG24N&{yL1`IuB?f_fK7oCE*knq4+zEtB#yrA+5lpjInEm#gQ57asb z83`>P446uw<%1h{DwG9I^SJy6N|zT{85m%TrL#bWA?1TsP(Ek@m4D5wprS9FPs8yr zpMv8NJ_*O8d;*TgAjunCj)Kg&09|xlg)pZTDL=XJIRta@DL5YHlW;u3C*XLLkHeXp z$sbL~k(&YJPmp;5t04ZIgD?+V&jdj|=fTb724#Xv9+7h1d%k1I;goI<=))r<`%4jxWf#gK7xyn!-YE()!YqGbraFm zfpeE5w+}=)xV#3rBVawm+*1hkPUz2 z7!086j*Ot{9r*;BK!S_8N-kQQpd%>V6X|I zj}2PhI`Ikgf!c|^pz^&3R35dVm+vlo4U9}1xcD3#5A#_#9^o@^Jj$oxc#KcM=?tHQ z(^)f13aJ#ZcpQ^SD1yEaMUc0-0<+&2{G>?)IZ4edjliWM5GpqJ9i$U*$4Bd!Y+vZ z3uyH+BHmg-@rG#MdkC?yFu!4h`Pk_!$d9mABqE+Z?17jIyEPX}JZ(fPCPCFDJf08i zh3H?8-Cw0h{&MEdLHG+)KZ3%!U>`((8#Fy4hsy*;rprkBUAe1}crKvWLZkzR0}yk% zpz#Gu2l(49%sfnp0D`t$AStW_8~G@Ptp9-25N`3g4$s%9FBY*&7AIh4NS~a8M*j891rt3I3D4% za6HOq;CPHr!|4p4g40<(38!;>0#4^)ITl}eG=YzS0k-DB0OAaIIg<%3T|ns@q)y>7 z#J#xNCrsIB=?EOqAp1f37eMuc%3F|Cpl|?*AApKiL(_*7p8!$e04lVY;}{7C1IRLH zyB%9O4syo~eg+2EDg#SVhz4Ta0g4Zg={(FHj9k|VJI0Y46du^z0V)R+1Q-}#Q+OCgO^(%{Q~bG_9jD&1-Tz=Zw1s|CvF!g%bVL9$ptX~H$e3-MCb?iZ;|5x z+^*-sY}kVmKB%4pxx?T+#2sn~bHMJ1huR6Mw?XO>pz2hi{w1p3PGII>B(mNHwWC4y zO!xqCPYTo?a5?J8CxPy-46OcQ3c)H2t*=4mGkk)WFAO!GPM; ztRK|Q0O@D=2GP%nOTPoCZGh5Fg7k?|gxuiaAn+Yx9w>c*!U0!#0t%l35e5d>G)xV` zet14EU|NTibG^8uka#ZK-bg%WZl*{it}8cqTn1$Rfgcd}WP==ov;W(`$P|Z}PeAHH z6iicu0RT3e>Ftr!SB?hu;u)z|u8FITuig-em=wc!Yt00i=HdRKF7<+z7Y# z3z(`178~9KRCsU)5K!U9%@jsJ3GVt3l&%fL85m%b3+!NDL+Ue-IP4O1PN+C2+(F{# zcbdb-cdDS}EvWqgQipzxxC@^_87N|4_M%_z4HHMdpc`sEoQh9LP0mkAjW5UtA8o_{ zIom5KH9a$rA-*6LwA(Vis5Gx6GdDFJd_YKINq!Ln?5=sx*)Q>_8Q^Q?ix_h9le6Q~ zic(V<;^R~E5=%16QscpACzTd}=#o^1l=7g|#Nzxsh76aS91pNIhWPm6)a25l%#zA@ zuyM)x`PrGN4DpG%#p&^&gT@$Ka&lZjn<4{1Ze++QC`v6(EhM+7i&7XoGSf2x5{pW(`U2Ti@!;99_z<7q zE6q#BqQot~$T20QD7Cnl0Td`D@o5F678IE3ztXIlw_0wRhU;)7LTm4q$n{nrwS^WTbfgnm;zIZrV_=zoZ^hkv=XScg8Xt* zkX9%+(Apq?@GQj2$85sg#Oh>3@ zMuuQmZwS;%1c@>-1cNAK44M=I@nI9dj0{0A=4FsLXk8IQ42%hz_G4lA%osEu#)CCF z85x3L%n8tT0wY5Vj5!%91knrQ&4P+DG6cbx%i%&XFs2N2xC){d#_NWPGJ>bIpss~b znBEG6c?;%hkXeCP{16E91I&pKg)kn>m5dC5Fec0$5QPv2AV^H7M8KSK6{?DnAr{6& zc3lJ(*G0fw2iold($2^b3uD5Z2y#cnd>9YrN=AkV7;`^7gkxb$SeQce!gw&BF*3x0 z>_WycKb(XLGBFs;hnWx44ibY(!zoOChFJ6&&WCG&GPXh8d>l$MG6cYw;_!4322~8F zPD52cfYOW%Au#46s1P$l3CDaG=RZ`C5i&9iH-~|NfgNfKKF_^^YJf!&wg3R_@CPXa z9TWzl85u%gMyf)sz+yYPE|AGE6M7JihMA-VH3_5)#zsUYXcP!!H5|j-4NEi33>*gY zVP?Vffd%1Opp4H@8s=k023UHBN-!|Mq+o`F+=Om6NDQt4%7E#Ed6T$+g89}8DFmPf z!l?;RxxG-Dks%1i5_vU>FnT5|FzBvA8P`(_O)s?h3?o7e*z48D0@Eck#g86$WD>yDI|j zE`+x+GU+U6G@OIdObmweVeI!%F-C?M81pk+C;-O%0T+saF@HmaKq(8x=7T#n2F8Rr zmXRR<#?*qU0yzW5wt|W=G6Xn4nZZz+ks$!a41){Bz?hkEp#T`O9xfCEV@`w%1;Cga z;X*Mm<^i}+0E~GAE))Y}A_5vCCtiT6eg~x)8G>NU_fR2FVhWlM<1s;}D;ODqU`%I{MR0tAeFy1n#C?i8Kj0syL22lu=hf^@)U`vh|83JHTSY64;5Da6&tie*w zZh#uO14=V81i_fFN*cQhVC4{O5}1)80LF}ln-yFGWwt%!8M68bP<5N2G$TU@jJX{u#L2)cz{8pj;=zQj zfH>ercLU7 zWC(yULFZ(H)G;zd9)QXofzpf&0Wc=Q>`0JeY#20b1`=mv2!b(TEAg-=NswX?hN)&` z2!t__H3njY0Z0=F!&EaeM8KHH8Y3_?!e%CeK)S&Z5Cmh!L4_brh4G4^qKphdFed2u zMvy2(A&fT-Bm^-L#sr-*2NH!S1hJ4Yrc(m3IVEsDrc(m3IVEsDrc(m3IR!H~F`W{D z%_$LBoDzY}DG^wl5`oPrm=T2OSsdYx9F!n7Od~81m>7&OVjrdk=4M6)9L@(Bh>T$x z8li%W48bratN>tUD6YV1Avp6hFfed2Fner(6b18PVlV@+blah}F~H{g7#V_KOxV&- zEV>Gy+F-Mdj0{0ACTwDyks%nygsr7uWC(&W+o8E1l5SzVKBy=oLlBI49xjAYjKdr; z7wQ;BhCmn-HhaX#5CUVuY-VH#gfU^(f*cVzAI5{(%*YT3V;5hj9F4AqkpZLBg(*Qwh{z5Gv0+&lmS;iTevmjaMpuI+cwkD9(l@dR zAT})5!cqk`KcTC^ViQaWQi2DWj*MZ$7GBU$1eK^THY}1D8A4zVfhBS*VN(f= z3_&m^tjh+f0)yZkI9Owjks%1i+ygZPq7cS|jd(LM1i_f!Q53>>67V`F2*xyq3PDVS z@vNYtj0{0ACTu?lL?P6849XTN!pIN^VVFU`&{IAPQkTn2#74B4A9IcOVL3JUh7OB4A8p&tY~3pe{i2 zTqHKnMPl(>B+Q@4o{PldxkzlDi=2R{|xXl#Jg!PrSqF-C>}7!%e{ z04am9o1n@-#=+PV;bMmKVQg6Jg4BVWgpA#x9N6#;BSXLIO!J05>Qz3Q8mSC>>hY42&1P2Q{n(u z5)ch#Wf!JLG!9%F6*HuW$cZdVYD$px+47#V_LOxTnS zsB{UM596636vCJmaG@X=(-JBK(F@}_LZcK*6Xg}uN&lfVBSSzs)QlP^&Bze23CcVU zr5PCl?m(Fzpfn>xz&9w94{lw62$ZP=r5PClG@wi~D2*lHr$QYA>*j!B8OCOXTEfi0 z`5zuQwNOEjLKqu1Edf>ta0;7;VG*wcbsk6`EJCA^#9-_~xR~L5n0H}zgY?4K2zOx4PlHzcfgFV> zwJmKGRnr~=lR1`WqR4d{i^j0~YLW*<}tl8RwGSd|A_ zWdT(Hx^Do)WMl}1F+r!!gM=V@VLaH}FqYOerqcppt_0m-12T(|Aq2*RIR<2W;CvVl zF?SFMV?G3F1P_>oz?d+{K=i_R2&ciq52}-aVJVcg6G}5O1i+ZEVRA->V3@Gyl@MKF(aTtAg2e-hw%_|_87*?LNypdX;2D;v7Mn}j0^!V zlQIxOut77J;UJw*pTQ{=sB)Nzj0^!VCalqn)P9CFSQr@sU`&J)f?!U7RhEnlLFi7v zQbfV(&&^PmFfs(enERkYkg$UBU?WY83_&oa6ttLwD1`A~bN`GCK`^F0ib5CL)@U`&4$g)m+SRFsh+2*!+p3PG%f@lv3oj0{0AW)@Tkq7Y;qGR6#rKy0B9I3F_< z07h1843~DLIJZk z16z`WXc`5*gXV;9P@0h;7{>ey7lKX2z|$4X0GLuNtrnPC*h+Ush9DRdws{^Dr$HFK z5(TJELnzJ25Cmhwc3o3hqXX0?Scd?VTcJX&P#U|9Fb%L;0hD{88ekfQKr0xPZKkkr zD5!8KsC{M(Qo`^tY{D6IT@^?>ma+_H6Ktshwh|XMjRQMVfRP~x#@r7w7E;r}mDKIjGz?gc_^a4=`)dieoH4Y;~AdCrH;s;R(avw6z zf*R8ar9m?Q$np#f46wc{tl&ZFtH8|0;yrYOv3XAbTEJ>TX;7AdGA*DqBSRpJ844AG zcm&3q4Hd-_FEAyIa3uk4P$ohbED^&!?E+Pj45hI-`~g%BwwQ{MAqd9&3zdSn62@bP zwxAgq0%1&8qZ*t|t!7j%QMd9ML#%uFcF z$Pff$E`$m}Tn*zbfr>IR1i_dq;6f2F=3l4~L@x)lcOwp^A)+uIY&r;wzqde*ISr*5 z8G>L;m@YnsYIxCA&45{Q!R!`>+ResL%}~IgG#|!yg^FVdzg2q)mt4R-{T0dos1;K_&sSce}L zdW;N#FgL;y0hT-sv-mbNFt8dPhKUIf z?<1IMMh4g%83VFL+|Ea6jDRtbH4@|0NNgH02YO+Cgf&VS8G>L;*xU&d!$b!d8+OPN z#NRL;Y>5{m14eTYW`e5Ssq5QW{8YT|q!YKhLOAJc$GE^aX5TlU_YqZ1K;EW7`Fec1}Obpd5u(00- zHIGQ`4|FwbM@ofbg7z6we+G6cbx$KgUDFy;%mP!Nm>I(!l2 zZbpU>A!rq<2BkqOPN2+4D9y+a0%In@g)oXyn4>1btqX)Pm%xQWVN95NnHfs!=fhkI zYfv*Xgu<9G7lYNo+zM;GGBO0hn1OJIg~FIHw}Q2Te2a`>u7$aSks$)cgza!(WC(^a zVHQKu1B?f=09y<}ozn`XL3I|C30pt}GAn34j0al*!pIN=V?KmxfGC9V-athe8G>NU z?@%F#LKu$~S}!m%1i_eGP$7sy!pdK8z1*p|LYmGJIwXRDydH<_0XSI+z1sdKnoaU`$vrfm|Me#Vm|UcM{ZHFdr~6 z7|(~XVLcI62IEQwnfWj-%uB2cMj&BOkRam{sEM#{5i`TYg!wQI%sOUsOt09yy+JV#au6} zF=6L{f%+Fv**{Pk%T$mRR4NflGclBiz?R3QLB$vu0$|Kcs1Ue(hB*SU)(2x`APTA; zw$hG?p_l>2?tscMF_bI7jDmRrqz*Yff!HuNv_kb^DM?_yv4biBI}^rnLI@VjhjC#3 z=4B`mfsNc5%3xGIuy(!-wAs$c5CmfyLJw+WWC(*X+n_>_xPtNCKt&lD!eC5g=r}1v zA&kcaJ};Vqfsr8$#uSDMK@`GxAy839hAl+@3MabU5? z$PfZ^d@tOkAu#3ys1P$l3FCYi2Nti43?VQk2h{nX4g<_fuvlee2!SzSUIHmYiCPc` z>KB+t7#SjAOj)Qa7#V_LOqiQNNhNYVj0ZCxOYSOz8s7n>nHdV}=fgOQ5Q25{J)nMp zIhvWFunr?n6+jjBLTN^ZKp2w)dN>XvLokfV1r-9hHgG-V?K;|iY*$T4)EQoA6hz+x064WqehKWA& zVH}vTj0}-5CXx+4^Rd|AgAtZ68(;y*#84;zV=sl;%EV9%>j;7z1;#LaFq@eeCMd$# zFjs*3+vtt}iGdA3A_AbHJPArOG6ceyfzY${7#Tuf%m}CuBu-$wCa5SQLkNu71s4j0 zF%@_q)?%s8ra>*=38g{V1jY`5n-qg-U(6h+EW&V%%?B`p-a!p#WQc(=m7%9$f(kMy zQwvHnGDN_b>2RS?7_$K`6wnG~_CaYzhEN!DDpUwuc)~awP!}^Ygu<8saG_wB7i-}{ zp)e*aXc-xTVZL6APz7V|gbM}3m~Y@hp)e*NJT3xYOgteN3iU?}lxAcIfHC8tLYxf6 z45bV*H4IAgVM4I50xgGvsw#of0t}@L3Iz-@#SBWN3`!tP6;K&iz+$R^Bpw8r0*y{s zY8{Y-3v)yr)Gn+fXq^h6q^H!QzdPAp$elF&g-=7+C_3tRNWk0aOT-4}<2z zc+a7tSVjP0X1TzX1j3jwGZ`78U`&{SAmanE7!Nze4jvmYaeWBNjcApI_x`7nMuRGg6^0>;dS3k4KHnI%vfq`QJaWE&RBu@kC7n~#=HeLCD9y+a3S-WK3kAZMx1mDJ45du-VH}tfnHee>vA7IlLf95+$W18C$`Cx6 zV=@Pf3p0+DAqXT4X|^4;6ywg<21%IH0m7P#WBffN^Z0f{Y9S zFec1yusWEnFe|`Wu!@zA27zbu7BSR#NiDZM%d@MF#wn1Pvzygq+p-^B7 zi!$SUn1CwOYIcTVhy>JWa0+Gu%nhK{2#gJL52)pV?i`R9+z=>Z0o1a?P#V-ZfikQ3 zAZi#Hf?>=Ws1PJ}V7%Q>QAUPf7;`UF2%-?iy95e^RO8XT)I&S$6~BSQd;33Cfr9l9&Pg6Nil1tCF!AYs;?fZ6~~Com4oE=Gn( z7!%1_pZQp<^_h>wTFk@)vlbRzpav|AeH-c&Yzg9RbFK&c~WK8&{>D$2+Z1!JCs3PBXYc)Or; zn~V$?)gsItp-{^i86sdzFQ{3J3?VQk%w~}B5pZ`PjK`=L_@I6P9k>nh0wY5NjHwNk zVq^$_G4-HAoD8K5#SAhP3`+B1LS|4YMuq?w(*-Idz)+0TR0C=9fXW0wX-pMmP?;_$ z&Bzb{W6psK#lV;gphCSuDBSVY^lxYp685x3MOb4hCGea@sd>F?SD#*wXoCRf; zLTN^Z02p%`R0zDP{R5Q40#86Od{Cw^lxAcIhA}naLNN|dW*U@cWQc(=OQ1ra_IM$r zJzfD7>V(orQZOM{8Us5El#-BfF*NZSLqnF4Aqd8V#e)Dt6$7Xmzvr)_$MIiEnRz}; z29~A;LCfq=mB6H6!NtfB1Y^Q79HzN28Cd4SVlGSy7QBoMK`2{RbWI2TOURj5gf452V4EQ2yK1jCrH zoXE%!5DJZ?cqq-t5Da6cL50A{7RG@kbEIV333q8Qj0sD&j0}M=<^rfHW`;t>`7q8C zs30RlFpT*cDg?F_#(^bsq~r}tz>EyRFy=C7f@Nd~gfS68hB5tl7#dWtGL4ZT5HlqN zV#YH@qZXFlE1?F;F%;EKPe=F_Sii zb79f>5TWZilnE==u{19ZK!fBblxAcIgfVwP*9kB(1jCqzphBPuC2&5B_Z=$A$Pf%; z{)Y-d6vBAj&~-&v+;IkKzq~Lg-7_$NQaX&S4i#f(C}H4YXpou@69|M#Ffs(enBh<% zxLTONET{w{Lr@{;ENlh_h8a+rks;^~)V(m5f~<$Jwc)M~a)UBMpfr|#@h7N9U=Ctr zh=eg=P7z|5IHex4ysTbo;)MDsEQ<4Ca*yH8jD#^^&c&t;CWr7!B#a635;koxxsOn9 zGcrWNm@r>s(*~16gh(We2@4r)+F){sP>X~y)1U!~O&d%u52}WdAri)fB_wRxAn6A| zzJf*w2Q*~)K~)QQNspll*Z==g^WhRu1?&u5|Na*;$js-53J5`IMuq?w(+esDR|^w> z#kCm7kSYd46|R5(r79WZD;X61RTX9C!{lM{Z4Od5aYp?V7Kl>hl!2Z|?< zkuatuTqqRA%z+C594iwiZU_;z?g{e2*nJK zP*`}tk}kv&7!Q^X85u%hOjweGD1`A~>4%Xa6vl)l6^KF@uM8UgSQ^H#aEAo}=(HSI z7#xQB3nbP7JsABHlm>~x+3?7ZLH8L*8M-GxVlel@QUpj0k|q%(ET-9^&H!zJfHJwD zG&4h?=X_}>Cm%{PG6ceyFkdk<6b4|-7TQ6*k_n|58G;L-Oqg$xlJj}ECo!6yFoPnY zx)~XQV9Z#k5H~|9!xTmi3l$CvwfQhxTA`AR3_-9&aujY$5R7>eDuiMLB9tJkolv80 zLTS)}R8Xb?R6ip_B+L(ptO;+xn81?=#$sWZ*$0Wjuss1PSZ2?J#5 z@O+riE2tD3LlHwYgUoyw|2tHiks%1i{0A2bV1>G$9ZG{HAc`1d=EL~>P;t-#dngm( zP$-Pa0SzF~@&+hV9!fJZgus}}aG_ur(+VyW0t?bs zDOlhH!pv+Jx&Bzdv0cEyA zX-0;SxlrbID9y+aav93}0HqljLf*+ijOT!^rw5hJQ08PP&Bzd{0o?>^45hIE*k z1sNG)VN55e5Lg|I;{z3BWQfgyGE1N|SRIU02Nh&wh+P9^?uF8f454h$M8XH9!CGM) z38)|=LoAG`02KnOgK@N=f{YBYFs3O~2&@jqv4sjUGQ>tenb}a9ks$!aoCy~SgfW*w zg}|o4IBTJTj0~|b=3cl^0E~GCE)@6_%7oVaEAq2~qxdE&=ew8kYrSB9e3*tipQ$ zm11Ow`vqn0fTku!hBz4WI8+F9Mk0*;0V>AG5cd_z6o94*Mus?1C{q(kGcv^KL798u z2_)_il$j2Xw>VfK+=&o^G3P>sK<$1Q`vX)A0V)Ja7_jo| z6;zCoAqunnih`A2;m`!g$Pk5Denr8GGFUmr%rNmx9gLF*O>T?~QLr=%E14J>qF_u| z8vz_bFwSv=R#?e(2O$Jw!U{8xuVAGaq8g5ZF=1&Olwe?NK6n9zF&hp`?}!ou-X0Ez zr`Zr#GKRHN85u%g$ygqmwip>gV99tMLI{=^x#5W=1eVIY5kjz325WyZGK9cVk2E}8 zguqe{13W52VCljDAp}bo2?!xrx_~t{85u%g$>KCZ6)ah#z|%koEDgY7kC7n+7H6>7 zU}Ok^MFyf&!B`Xk3uRdO%)&6Sr+z++hY0&L*OVPTIbLZV?|k0?T-VPTIb z)1xuVhG4i2NN5%ioC191V*! zL}reLMH(VAN5dj*BRqzpVUdQ&%h9k%L$pPrVR@Mu9*@zmenSI72o|;55kj!q2bRql z8KPlKSSDv?n0Nr5<6-p>BSSQd39E)!7$#1rpAX|9@^>^Wer^Npmb9_l*oUbr1g2{T+yNmlCd?ov zhFZA2Fb1r+fTdSPh8S2_zybn`{V-iR@RB6}#zYttf@u)OE~*kg}|7wa}pRCB4A9| ziGs`w74`7r1z~qEF)~EJn5*EXVYH=TZh)QN%E%B1W5Ny;WMl}1F<}ORA|h};jE68D zV~GK#@ewfNVY)$PMPM-tV?A;q)EBVZMi?0aU`&``V1u(N?wK zFg7e?K}%Dh4u#zy$;c1@b093JKsuqS;1s6m2J>O2BaDLIv4t?o03&6=Zf=4F86!gg zj0r2e7#V_KOqk=aCQ{fPTf3p|WMl|}F;ByV0$|KDP$5vHV&+tIvzQr*YUaaC16{lh z(hYV6i~}*`xNSa*qPIe3?VS)Z>SU#L!k(ajW~T6qom}4x(0S?1S3Nj zjOh=RVq}PdF-xFA%nVgE^I;s=snm=NQCFbyunR#M8G>NUuTUv)Ji$1KvkZb@Ofz^` zV4P;m4>baI;|U`}42-!3Dg`>)8p>P`rI{HDSmwhxuw#@N83LG~7V$x8MutEb6L#bw zsPKi#>OyI-X)q4Ltr+!~GSqh1dDDyx0Wc;kkQf=_U`*I`o6HOo*T8ZTG#(ikV6g~` z7tk6usOhjPb{H9=VMZa`gfWE$i))x|SY`@gb|8*Ljf6!r?8IzFhDaC_7MdIk6$~5- zs)kbYVXk-$bs!@{AS|w7;f-bZ5oT*7To?S9bXWv4G6cZPhWQXQo&pafC?geW0?cKM z3=uD&LWqMOF(M0gK1wuHDQM+0lnFboosl8n22|)Cl;&nAhF{wow zx`hFv5XKXRE;nRk2!k;dp+XRaFdjE_;V2_R7>o&9WC>9SUK5XN(XiZU{U!I;6&1!53|FkT~6l#w9}#yk!cf+&RXzCcA88Ny&p zMQC#vq7cSwg^DsVgu$3Iph6IZFy1PtC@4KbnIX`reTYIBFCHoiI?e~ml!MM2LlnYz zMo>{khA^YPTOW+_e7#oq;V_?iSXrc!x zgRw6{#TXf4U`%dkQU@u+%n>n|$pxcJ1WhFjurvZ%iV77&B$r@VazVs;FlMX=W5#+g zmROg7#X2mC85x2x<2M*HeuJ^ZuLNfN24lu=FqZh0z>MEuEb%LW8Nb0;;#UGo{7PWP zZ!nhlmB5VOU@Y+~0gGQm@rKdVgGL-fDD>S5;22+Pl!(yC?VWI{s%3-%` zfW%;NJ{wvhfy7|-eG9a52Z_PhSK(p?^I_FJ?05u_I`n!NB-RS8S9d{akk|<*`xBG~ zwE@x7Hb@=1e?CD4U?(p#GQ`13ZA40r~2#W$n28{VS*r8~!U}k2R z_@E9}kp)2m94r_Q<-kH3EC>thI_SwBV8JO+&T1$P7Tg8p_(5X@ESLc0z>XIH3(kTH zu7J{v41q8vEFL*QuAIVB%b+wLCIpLGuo1Axg&jr#7Sw~*amG-Zks$z<#$ky7tPaNM zfT}~X437DP`RU_n^&fR&11LG& zz=E(u0xQeFg0N(A44QI~T#KGu!0Mqn6HduNi!4JZ4a$Tt_EuWv*Tr7D73&;tnhbi(Y$ZVE`6{brL=x3&P?&7n=LQDzQZG28`tm znB^tBl?%(rj0~|bCag99HPHg+!%9q8Lza;N-lm0RZAOMz7!wvZ5WO&76+COk!kDml zfGC9VV7Zc!Ar{7jOjsO36vBA049mz63uD5f5TX#qgZ0)J8De2fSXPE8gz;b*n~@1SMusr-7AGSE z#z3qm^mxG~P#U!20oD>c0j~T-O@wh^S8MM#=R!~Vos~fONScL>@hJyuRoLO*{od4&;IP0N;j0{0A=4QB1 z2#g79!h_9&aS#<8=l}UI4y>>Qt3jB(2GN`i8jzw z*#M;(8A4%9SmI=42t+UUz*=G1WhOi?P25ul<7`6+_SC~Td!d4$!}Xv{SosHz0vHFD z`@q(q7kpqrXdZ!6u*?Wc$6!UU1j++Vlwd(vHG|Yf*i#3~qcAUnRl*I2GVo?dq^#Cc zPjnW-nhCI`W4vh&);RMgdk-w zHY_`0>7Ig=!CEh{ks3yZI2e-^nk_-fU~E`o1$zRPe777#mgyFfzcq#)zR6%z5-2=uj~1k|9P0_5`wSVm@N zC}L2kV^Eq86SxXhz`{_)AU_|*gJo@YhKVogrm)D*hjrXtpfx1i9GC#CN6*Pn$56x| zqx_eBK1>K!G+^-tObS+PL0n-8?S{d6YK#m4Fs2(+ijAQFbb{i17$4SyV`K<~b>dz^ zm4Z7zuu`W3+L?n`1mj(Rin42lp;aKnY$k2f&zGP$6c9i6`n{9N3Tm z3qt{e{CpS>R_CL|JFGGL0csK>L)?65V|Fo=W@HF}F|Wdf;?Rpvi0>kybug@F$ph*< zPhpw(p>7I`%zT(Atg8tQCRmXRD_tQ5!HU;MPy^w?Bn{2@22h%jApq9ouY*>0j0|xv zpv+HD8p~i2dR+#}Zm=`~tI9xPunIRBxg2*^NK^BFmsVQg3(3)T#)QxUD_i7V>R zYf?6bi5gQ_6yUWfEXRROfN|X6`6~d%^nnU-fadF_uo%kBhl%JQTG1=&U>qB$AVN7z z1gR;#q7K%ShSkMjSHL*1!^fazC^OE7i6E-%i7V=099TOT6lO3sD?H_f!kDmH9NgW6 zagZ9yE9&f^n*5R3Ckp4%`gs9gL*|B?076#{msY_3S+{W)nHG;IIx@o?wG(h zu+$G$2`eyQoqVt$ywL__5L-UL0t-3kW6Qu$W8oAm@4&JF=+ICY8`d6VW|;T{+t}li zI`oPTq#IVV!KOsPx?xEemJq>$u*v||t^*6g8h7uZkpvcmwYOj)4i1ItvWCx&X$8%@D)X!uW@g)WX>8 z(Ed0~Ev#J)D;Hqmu-Y1y)nVeWY8e(}Fmd$y4^5CLN%wbee{auM35 z0t>><)L#tm*gzcw6W9cmU}Ok@F=2%**Z^3bJPS`;0Wc=4&;+Z4g?%tII>3VH;R+Un z6;Lo5)=5=@h8eiq1e@5@NS^^7VnIAq; z2fO>=7*r+bmSJc(!BR6=Gb}yBI)h+A^hsb)X@MnwpQwWsny{n~)(uPiu#^rKgr#*@ z!UhYXCvC7Gdg2BPVu^+mb?6BktP(wmg9XuxO|T#=_Fy#nq%FAhh9wp|>R}@Yu*MQI z!^95ws3B4;cGSaSF#r*Z9rdt+Hy;`|VD0E-4OkF;iWWyibkt*sh>m(J5z$ePB_cZN zu|z~iJ(h^*sK*i!9raiuqN5&5M0C`nS7~5hVu_s&j1}a{(7(u(d5kUCi6fYJ8QEDl?U1kwj%!R@KT z==;#J?+26y^8C0Gt7sH!TNrT3<0o05|*DC83HPyMPwh82HP|V%2^4e85sg# zOjsWVtPa+b6M^;z7#RX!N$n}Lr34xRfDS&)fznt84qzoKtTG2(zyht$Vfl`cAr#g| zc?!*fj0};m;j}f-w8Y2|3S(}D3kATKu$;-r5DF_gVWlP`Lny303A@jNks%bu41A zT7hPUpcNpjDFWIA2#qUPsldn(3M-)xz@sM=#ykQS3V<=i;4u~oV@kn=0$@y7`GVBN zhDA9eLnv&d3Rbo-GK9jq*RYa>ks%Z|L~Z~dTndFTP2fTSFea?@0UbgGm4)?)uxuHD z-FO`V4IxH`02mV~{9ySOMz4lO*9j=i$PfTy!r~cgMFXoLVEt)Oc>znXu*3!uLr-lW zF<3R+3~hgc#9-_oxI5xuOjx>OWC(&WQxU3Q%uKjY0E`Lq03$;@jEP97K``cBxW;%G z6IR$VG6cYwu;dJKD=a<3mdk>~U~EL{je#|sVM!IF482DH5`&dVi1Z6zH@ya)SmQTA znLnX4BSSoliAb3-Fea>L1~MPk=!T6-Yk*4EeX<7_ke8t0I7k_c-3QHJj12KGW-dIn$HJJ1!TSIh6P6Je z8De2fSVq8-Cm0#xVOau^{NrKC-w|FO#lx5$aG?Mg6P8IB8RCh{Bk-Oltjh!PIIP@< zWfn$;SQv8~Jl_Psn5W@FF)$`r7RH2SDWq;J{@fK0%V={ExeLZz2p0-~F<}{x zks$=eMC3vE3Ul9h&EQ}1XFeYpn1tUW| zj0roLm60I;#)Rc4MuvD86PBYG8De2fSWd#yS0^rhVA&2vlbij(14(Sq_K_%*W@HF} zF<~uqusRsW451Fjl!sw!tyVy(g6#i zXECrKdffpQgjFQiat6`>_J%t2Bo5XMt1e)P5G)9*3Sh|)EQsE~0Sm&KIIu(p7K9bg zuyg(w=2^uS<~|FDU5MusGGYq=Qa zL0j1$>|vc`m@60=l3+|&!H=xm6j}i!Lup2aWEiszD#XiB3cZ5CSfz+T2`1bLmo7k- zh6%%J5nhIZI>>DfVBN6P1)Dc#WJrRgBUrV-$dCj}MX&_K$dCjprV%})WLN$*_>$4sCNI`yM6+ixq_21AAD12{!Y^$dC+6U$CUb$dC+6U9g0OFyVtetStdc zUyKZ}(;r~zhXFS1cMsaXWn>6|F+W24q|6KxFVw*}&Cot3BSQ#`3G0!9FWrT45Ph={ zSnSV*#uwN;7za_uhrpPy7y_$cy}uV#uR{dpcxqgU`%niPza1E2^R{0F=1(lks$=eEP$#4+X>^q zI`5#)8&v)llxAcIfiYoyc5uMMIIt!JBSQ#`2}`D6D`06AmQcZhFb*uKGBSj~n6NHC zSRIT5ORkIzAu#55XzB)?^a^GEh0=@+AuuK^O(U&ExmFeYq)7b8OmjQI_!5qz&Rj00QY#mEo>W4a((88D^~Tqp#_L^NyyU`$xU1{^>z z4y;08WC(;YVJ#fct@2QVVc8k16~=+B7h_}yfiYnhU4Z=nsN*25Djn);a;32jjq6`dDVA!0O;d3zUJUgn<d@6Ei1Rf9H$jEALTN^ZK-dy)L|qvOtIbwIi$6w& zz$j>8hNy!AVeK4Pp~lD%2rD7F;W-~;n{)}Zh^~Xuj0}l=Q07D^4H{*GGA}@BMuvdZ z&|%80P@0h;@hFrDYbG)>1e}2iDZrf`0IO9eBZOcJMiCwifEDFO5UMae7yv6`U~Mi& zh5$ckkpU}c7#RX!V~wz3M$lcv(Da)Ir5PEL%Am|jD2-*<5PgP_jp5UYD+~+_uxUbA zihzkj(=VKY>4&X0K-!bu301_%5CdaQg9`<~n6scl;FEn|oGnm6JPuBPDgm$RgmJQ< zf}m4lpv?79nvo$8#@q%Mii0uF!-WE2%xiF=I2aSQiHeaS5SB|}d!w)%f&vQ~nBzdL zbQl}qP>h2NilFwEL1{*Y02uQhTqqL8gw5=N=VxG%_5hkw!Gf4Bjf6QG)+z<7gPIMe z+Mq0$4U7ySF!SF-&1Yl?fiYpj&jJkf^`>f&rlpYz=uT^x{V>ZI8A4!8*n~5tCRkp9 z4QF7gfR%`_eh;P!Sg8Sz&c~Fei)_-XsUrzuw(=~B@RnQ zfhmEN5{wLiFeWU2GBQNLn6Q-3%up#YAI5J89*nGLxo|7 zv@tRS!WC(#VkqUYk6L#bqxI%z& zPQgtJ!1N-<$rmt}y25ou!kCqCp(t3Nz8NaS%uvWOAI5>DNzkHxs65O^V0D;&iiG*; z7F0bWL*O4M6A>L4_q0F{3TDWGDrIDdhB09l3NRFlOksf}FC&#o1|@iMKv)$GW8R0F zgQ*D?a4<(q8YN&Bzc0W9mSKm>CK)=EFGNP(e^W3z`q(`9VdooKqlnW~_0hKe~tX;9}C%5;L#kWhy4 zV1)%ELlBHP6{?VtAq2*p0~Lbkh4EmgOJZ>v%=iYVM$i%mbZsCps5G2H*T==c{Go|8 z9V9;=T|crMNC7g2#d9K55EMBuHmtD;vLD7qxYGz$;XrjVFnoZqpfLh6ivh|Og3=(f zV9H>$0#puG88b2j!I-e>7^x9?2C5no#4w%-yxWLzvSlSy!)7SW$Pf&x-4VKAjc%Bm z7@VQ1v!OI2L+Csxa|x7YWC(yUPs4>mVJ?Cl1jxt`3S+{0!n~l{vJ_M}6x57W3K^7O z!V*wFh#*NbFM!6X5=>SDstRaWD9y+aOnrMxpoYUN0R<_{7MP1cVo+zosSQwlr=T<=LlBJl4K5T4WB!H<1;CiF z<(Z5Op(4=XZE+~g$PfTyy1|7)VN5T$P!Npi4Hbd}EsW<26=h@ygE0f)LKvwS<{_B- z7#RX#Oqec^@t8;EVrq|YX~!R-3dTeP55`Fr2~gEJP@0h;2*zxM3kAcNZBQXlLcu(ddID5-Ka^%<2!b&= z;iHDJFyVR$bfOe38v|RFwo&t$pJgE6;3 zr5G6kU`!S0GzXT`f-u7`5Eg#05MpEqhB3cF!;h7rn88qOK8yQ%7Uv3gfY9|LcuVN{9;GBSi> zIx-X%F)(LSAA|)9G=xz>`=Lg_yn-d|dO?-I0)UYr6vjLVl>&Lt zRBb+t3-doXJWSQFh6iX=5$aS#cwjU(VBzr-Y7rwtFpLQcbY_NPMz{?Kqc94l5~vZK zP@0h;0LB!CI)srS2Ih2Fa56H)z?hD3RRJ(2!nznp2qVaqP-|G>x*}j^t0076OdY5Y zGebqqd>992F_s;vuoM8Zg^?iw7WhY?7BezL!Qt5w3&{l*0o!02*RxP@0h;2*#WQ6=G&6 z%9s!1Oo0kQN(HzvPzKDje5fkWMa@tqvPR7Aa~4!p2b5-H2!Kt;!feKprO;i9r859? zNfq4SKp3+QDg>TEfpHMF!NxG5USnW@xe;b0Gee=od>99&51dz(B``)YVIdO*wUmjW zNCd`)8O+EK0As?8Tqpp>)P_z` zGBSj~n8r{ca1uNPAVTR2YvBDvG80g1H~&97cvf7!&3U zMut$BBc8z>5eR3(%?@RRGGW$&91%Dl#^Zv@f>zi;nFx1bRvP zhMAlTH#rE#TnZP$D6wEBBP_@0mco?4Ok-rgFa;K+2vacni7+Lw!@n3AB4Nx}sPmW^ zDr@G$I0)M!Vayt+I!1xU_F#M2TEh6D$uiGK9dG zuyO%n3DoIu>NGTj-$Q9eh5(rTh_DQRl@c&Bu@nvap#~rfioi4|0%j(>h-D~|m=EVb z3m#Af7^EzLrLe71F+V_sAPQkTC8)iO41q8v zvQscy%NkH)jG#0lLo|$;3KxoiF*Bh;;7$>YlLr^9u7JCEI#iI6Ap*vPxty7yx?(>=&UmOS!XS(m#eAre8&H~&Ar!`h8N|d; zU4b49pspKwIDo`p0ig~JBSwZ$7!&3Q&;liBQioG8&!OT!uIv>VC1bq<9N^!XH zK`X zVN7kPjSz(}9>N_MEqhFNM8Mo}3hs^&7!zhQ$oL3&kZ3|J1r6&%nJ}9n3Sm5iJ20Bd zK2Xb!Kxxpn0w~iQY9^LA4TUN>38fhsf?-aCMGhlFFhA4)4JeHzI%Yr(SOld(1D-JU zI;a>ULm-T~3oaDI3pG?6N@FQ33ZbeIev5>;5|&c1%&Ebw-wM^u$Pf)ks%7koDG#?W2j;%Vo;qA%qE z7!G0GfI0=?foOECSR&W~n)+aeyfQKb!I&vfDJHQzRFsh+1jf7z6@n;)@nCCG z85x3L%(qa5j0_<#<~yhmL@$iT2VKO)$Pff$!q&(!GK9dG;RsbQW(`~jqdbFJ#GnXe zFfs(fm|NjO!7wJwyC8Q3!aaW*p%BJ|c^9G(#(RTM2xG$h22lv(!TiF=5Da6&{KCi( z2xB6=h0#)hTEvh9Wq^k3pv;L-nu%ee1B?xGAjlOF^I<$#1Tiv1z?d)xLKMPyu-lv& z86sdzn6n`YVLX`285tsA%p7RkKor7wFt33e33V?9h45P>j0y7(6T?Ig7#rprkOh%g z{1%DLZ;|t1eq(~VkC7n~#)SC>Vj_$O^AICLB#a634MZV~hvYX8bWb%vZQBW@85x3L zOxQivAj2?M{9tMfglU9b0L#b_3S%Ohh1q4mG%EsT7EB{E!^9o1T?lYLVCsv6>4V*T z&Bzb}W5SFA`6m)%3>>yJ^f^2rf?-UUM)08ZM8D6BLC|^y!;lY9&2rF{U5pGtFy?Hy zP}~|Qa~+gsWQc=p0lkJ0f-zyM5JAqyj0Q{_17S8Uhnp1(W5R3(nH4x6#)H{_W#tv7 z@ewfN7s8DXfiYnQf{c&AVmwA+h-rKz%y^iiKxSda76a6BS11kYl=wl}2~Zlev;)dS z1bzU_jj+H6>D&obb^}U-#9-{NP%%b^03PUKCYaN(RGct(!Q28e3C2b^A{d(^U`YUG zF-R+njj$1;;1z+o54MJvks%bugxMm_@R>1^Ii(r0R{k?%BrN&D6d;Vp=msc3jn{h6`(=x0i_ukf?&*YxKId;Sp^jWr5Vhu zf@xMD%&ZEiMn;BE7!%nn%n}RJtO%G{WpJ~CVN7JRFxSq)iZgjAgOMQ!#)R$OfOrVT zgN;=p?aP3*pFq_Yj14O;A)29vV^A=w7#RX#Oqe}P3==tEY)z;}MutEb6PBAn%3y4m z-AoJ>@a7%N9Uyg3Q!pr))r<@gFec1qknb6wLGTJB49p?h8rIWW5WCbaz_N*XN3rbFec0|5QQ)v?9v2AhENz2 z<`;-U81E;-L^u=b7l=YkpJ7yAm_Cby`3x2wAhR$t@I7d>OFKaCd5Ev8YIk1f|4$OBTr@`28XETJrnNZh) zl)-#}a4f7+fkp)b1FVbm6)M8W5CCJwz#SF_OP@suA(->0AcSBp{e=+1Oivi?5m=8b z4z4Q(W?czF2xi?hgb>WSKL{aA>o6KkFzb@wy5eBgRUm|5OqjLIpfxZs4(u)xMus?; z8(@u8&?QvR2!Oc-tQE#V#00E5gN8c;g94PL2c;Pqf?!P8xgCrQ!7wJQx6H^81Y^Pu z5(DKv%qq?WYAj4Omg*DM+g<}zibcr=s1$4@iIE`)#zY=h!L%N#Z&*^Wvx;Di0od++ zSVg`7ny$E@DFZzaaOv-cDx3|a7ei@u>Oa(cSR!MG+Jno!S5N~Qq52e{>bs#dR2ojf zj=6yC!iDX$g*7bT3ZaY|D2=`kKNo6WJCr^TrJ>5u-&J=P`(zl8?XUN&x5Y@ z-UFpiLTR``P{tmp{sT~26RJ)JO1nU5Unm_2rK6!V)EEQ>wVw@2qjzG(pyJX{8le%w zIsz5H1f{P*>BmqSq6$Htgo?w|!%hRlrT!~a{SPS35A}xxl$L?gYET+s6NFU=6^9*V z1>2D`0V+NPO2fh*q6!CEvoAj&aF6R17bP}&hn!&)kVP<|woMmHc9DxL_X)1mZQC=K%u z>^>`W{a2vsZbNBU`{fCg4;%9N0p+9XhmB`QL&HZAO2bASVB-juP;qqqOQ7Pbq4ZiP z4I5s8<*O4=adiDppyIEg^am&ny9$i~noeOCjiT#^B zfYKYFG;HS+Tp^Uv0bLZe4@w_~(#N1QR2iH~flk~PLg^AHJq=1XK_{OUKxw!}D1!qU zKz2~t8A`iBX{a(dwGS%(1xo*f(y+5T;R>M)*cAYw(DSvUp>#ZyPKDB0P&yY%7eMJ^ zC_NEM2Sd-B%!1NTYv9xwsJc5)`XQA53#A#LYh;!hlqDWX$TKNUVxg%3pGa!O2f=YsD!X! zr|g+SkG64u(g{$y07_4Q(lGa2fVu}_3W6kL9zrFAwFYXgAJjaUxiJ4~L;Yz2r6IZy zj87OT8&2Ujr8le)xf{7Ep!)VgX^1WaDG7C- z43w6K(hgAiD^wpFY!(%v70gP3x-T0_7eVRCP#UZViGUr0WDOOAotBdZ<-&x<1%}Jm~siC)jX7 z>tlW>4LiLCUA+%fJ{U^F4ncukBM+NLTMdaBy2k`)eLEdW!wxNh z%^RTWPlc+3oibhy<&$e3Y$@z|=u+6NQ2G;;{tl&KOIFd{16wiD4qdJ>8A`)eh@h*7 zE!}~w9NG(2e*j9OtB3W^VC^VaKMK}aM^}%|*GtRHNo8PQuwh_e`1hZIA)PU~s3avP zM-SE`hiZVOb4CUQ24-knLZ#qTDFXw8fD{9`NyN+us=yHnq0zuFiGhJZfq~YhLG;1;=b%giY4{vxU|{$liQg{p8V3S)fztH?0_IpUGB6YnFvp*ffgyo_np8#x zh5!O;su>v=KxG-om$1`ntjP}7c?QIwehHmHH5ECi|s)Ldj{ypAM;V8$~sFl;b| zyLt(jm(UoSqxBqhRHH8z{(BK&O>OqQJJ2bnU}7cTvU>jnx2^lDM3Kx zuK^DOc#IJefGtc63M$oaoXzoEI z4TxQ!1Pd_?PQub5$WM@T2$zC0VCfK)`XK301}+Ch+2c4g&d` znGt(~3KSO?@Wh2)RxObuA?A0`F{(81oIkO#S&fdO>D&;t}TpyR4OponBJGcXvu2gNBUc0fKw1V1!E zGJsAFf~Zjd#}EU<9FPU9AYBUJI)i~>A2S2E!vc;SuzSukGcW`|)j&*oz|6n^+6#?v zA?T{u1#HOXxUw)X96%ANXJKHt02P6lGlzwN0pu@`BS4seVHpbp!v_>KpwT%44v5JR zT^B*-polzVVPFv8Mpgp~n+6mSW>yA<34F+Eq*)ml99W@w5bRUXVI2be$Z8x}85k;1 z)I_o}Fl<0klgG-yZ~#T5la+zt0gA{{P(l_!Hu(fA1A_vJ$QzJ*P()bS7#JE*L_o(j z96%8<0jUv0HrbtxfnfrQNHiM*!vYkMEH(y)4JaZlYzz#b+W|pw3JK>$Yzzz^P}J;W zV_*;vLbmHV8v{cDipV=Q1_lLDWHlV@3=9fl$ReQA=N(W)EZ7+s5>Q0K*%=rbP((`E z85kCzi1dP5e&Wa`FJ@<8FpxwR*~QMl5P%|b86+ZwtmYLvq;-MtI~xZB!vqvH3LFd! z8&E`SIT#o&pokQM)W{&43>sUxfFc6AR_6hV$R-X3h7Twr*Etv%7RVx-^95uViiiZL zu#-bpW6sII5P%{AstY%uh*WVhFnmA}najz*a6lf}oK2hz3=fo{`GOHtQiC%Gn1G}V z28PR=3=A6xr~z#w`9MI8G#3MdfC_%QoVgenE)Xy$nTvs80Rc5FTnr2o2&h@Y#lUcY zfSSWx3=AI#xcL#NuRuTz12+SMfFXXrgD!I}AmAQ*ZU%+~0&2p!85jZxsLA7IU{D~S zrk9(6p@D#!)!YmW4g}O3;bvekAfVHwawVwtV8)SF zWq24E5{zNyU{B8$poXCqel?(LHx&q|0bLzvKtN3o4+Db$0X3itNk3@fch3PH1_lQL z<~-zKV7Nda44HWu7&Z`4qruC-5I{hUA1?#L1OjTBco`T32&h@d%fN5}9$(nw{TeR= zg8>0^zVb3K6cA9Oz{kKafq-3Zd<+a52&k##V_^6|K+QZp28ILhz8>~;xQma0VF3X( zclj6?3gC8OH|HlG1H%PX{OL!CpMhb4K7KWp{0s~Zy7<*3^D{6Q5Kz;@&%h9%hTohm z{0s~Y1l)6#pMhb59)5Fvg47T&M?`>uVF3YiKo?tVAfTpDfPtZbfSOqX3=9PX)a({u zU`QaK=C%L>!vg|p{s=HITp*wZbo<-^0%|}vawQN5!!SVxh5`a=Y6KY=0tnbON05QR zfqGB$iVP`KzaBBe;RAv2Q50cdxIjRShX@110s>)}0IFsQq-W5HJOTvNOb}sUm_Wd;r6LRr z4FuHe1o@pne4P?uU{D~?etssxz#u@tE>=+nh5`a=ltmdBJ`jj6TTuoE2LkSi1DQi0 z@6?JiFenhn%dyfji5FvFFd$H7R*Eq&EFhp}o)`ne1p;l7{bCFZ69|}dTa1C>0ReNqi!m@b z5J=Br;tUJ{1k@ObGcXts2%kW41_lQLYH~qp2=q_;Kz0$x?<+uVCeRi=BF?~YfI#>> z1KCAD&0lc_h6e=PEGfajU_c-b8cHxQ93arQ3Y1`A5FntYQi6e@fq8MnRH+VF3X(Hj)es1q9TjNir}b5Kz-D$-rPhK+O_K z28I9vYL0@|B@j^aM3RBw1A(;4D8<0=fPfk?DF%iF0%=TJih)6aK$`cKVqg#;pe9|4 zfnfpx_kiXG4G5SsLyCbRfIt}q8o6{JV9t5a`V9hV-byhrG!Sqzhcp920Rc72(hLj< z1k~6{GcXtsh>Hkm1_lKJY6_(p7$y)s4*IAilcf7#I!^ zP_s~ufnfuI@YyEE!0>=TJNzQZE&^)af$SonhD)A-VF3X(y7CMR0tEU*KJp9<3Ix<- z$}=z+5KvPq&%n??Kn-a9paTIlE9Ds&5(xO~usj1p0Rc64{U`Qa4R_m1*7!D9nGhK;+A%K9I%}NXm8wjX5rNqEsK%kBE2&9HUIR92+VE915 z@1n{K3=aszyS6d|LjZyHp&Q5?0%><|#8UY#@++ zHi7IS;IA{v3=9_t_zSdt;R1p9`mfBuaDadsDbTVq0)97AVPFU#keA(57#I==_&r92 zfnfpxHI*t13>OHvXQB!N!v_LkxB=uI0{Q){3IoFd0_Hpcxrcz7pDGLt8wjZ3Q)OV7 zKp+q5t1>VwAfU!ym4V>{fwDJ4m4QKkK)iRUGB6YnPy-rYJU}4ccc?NjfHs2RYd>F9 zWnj2Kz?}CWe-X&1JZcOK2MDOqRbybdKtPR`8Uw=v0%|hU7#KDXPy=c^I}oUs7pgHZ zOdw#+b~Ofu00QP*RAXRhAP`?K)EF2(5KzOc&cNV6AYaI-GcYg^P-CXfz_5To85IOl zL%=;b>I@7M2&idSXJFVsK+Q6B28ILzYL2KgFbEJ>lk-fSfgyl^IV>6s3=Ra;C~7b; z6cC7a6AcE22L#Ls1ersiJj~EwU}zwqra^;&;R1m)HV0%Ef%0c7$Q%M?_(=^0h64oL z^Fo7xA%H;mFl#a}1Q4)GOp}43fq)uqO$LSs1pMW!$-wY|fH{$x3=9PX)Z}V1Fenhn zJ8d906L8ObO$LSq1j_l%AT{uH-PqU3+ysRo0X4rh85kxIP$R3wz+gZ?4QQ>F0|7Ol zHi7^FHK6!RAfN`+PFEnH=Aae>LjwUdFSQsL3J9p-(q>>_ki|dWZ30pwhhI&&HUonI z0X3j`$N*{l=FHV*U`QaK=9o4ELjVCcKL@!-5x-rWIt&b;gJbd4?>agR3tjEA0KtK(rJ_7>-0X3oe3=9PX@>jP$149A&)?mA+G@g!vX^4 z*cmV|Tp*w(-+%$UULBuZa|{?59uP3+qyYm%0|7OE3>X+dtL5?8rEkcLCC77$S5Y{bCOKtN5J5d%X40X6+b3=9GU z^66G11_lKJYVH~_Fl->8hS`{bA%K7y9b*QD2?W%H8Z$5$5Kz-#%)n4UK+S4n1_sbL zEWY$})0lywfq*&epp9=T-EC*ez@R{&{z@@rU|2xFoNiMFh6@DLY&2zH5FlXJbyEh0 z2?WgfXUf2EfPfkmGX@3*0(NP-APsz>q+|?~x!i1mde6xJM4ee0IAV~p2Gk-{|+R>%m~_b4;nausDY87a~M9r$C^MJeqb^X z8g}XeHw*04h0>DD9JC`B7#JA1>=+mlz$YQ#KHWmW4s-|(Y}Yd^c0h;dg2I!T5p)JC z$gL2YK_n@$16s5Ii5-wmOzZ%6At)t)&hG$)9jZEiI|ha~j0_BrkOze-syv7nZ^yu( zf+OTX^01TuI@u9o4rrSPNCtsnDZv1bIUv=b*by*;jnd$b9Yc8RIq4JZXMFf6cRU;v%00S;A=5{BJ&kd_`o%?~>Uh6jqUI0Y%v zu!oeQ2sN(ukaYzJkz#wu%p*c%o;_rq5g~Hg9yqk{}UP zWOIxiA-g0Hb`?27)(t{LU}+xIJBFlrTXqHpTTrmUF?neocE&W^7Bj}7F6eVB|D6JY0kyaz&X*H!ZH@6Zaxo&cV z%(jC<3|Csc?a09JK^+qEkP?FvbO4+NvPh^CWCjy#4%mf@ognM15F#&}AUg^XBKppd zeTWE=GG_(`22Es>uRBBbARyEzxC#6whs}+FVFHSXmK$XE5JFd<8)QBg zA#%|Tvbq={BIXX+0}c{lVt}P*(CP?Cdfp<;zyLcl4IJns)v%z`xWV}gWEey{oP?FD zp!0noz2bK-LnEAvZBUwpopAz2TOp@8G@Me#+`v707Z>}2Lr%6l7*#K&{mzw{GMu0Ia zC9GkFm29BFJFpa-fTaW(R#-{^m5`Vv7bunsh=}EWc&SyCnU@})RGOBSS_F$?1_p*A z4+e$}cF-`yl6pZ7>-At@FtCTJ0SPdy^k85}KoL3S!N5>}BJ#w8fuR9Kgv*nGVFHSX zx+ep}0u&J+PX>k!C?eUO3=9WQM7lf~7%rfQEb(Mucz_~u&69ya!2#xGSW1BS6qFK5 z85tN*Td1&<0E&N5I|5r7PE<;Oo{R@N_X?8VL2X%Zu)zsfEZ4wec>)6i18DgeTnU%~ ziscDJ#PUQ|SSz;me1Qd~ZJ`4;C zP(%*-FfbfI5#jV@VA$Y-Y)-H*1A~DZvd9!)&{2HIB6ocm7z8|#MU?#*7$$fji+K1k zFgSQ4i_Gz3U^svxa?+21VSx{_8g73Eh7BkpKK=|02T(*N`7CP=F$$7R11?0YxMr zh=E~20J5&iAO?nlAY_p#K@1EHk;oz(!3+!y!qCzWoPMN&85j~`k<}OnGcYVbQR5ZN z!0-V@qyTi}t_ZR@b3x{ys97J(z#ve8Z1U$|28IVU5D`%BWnfSXVPI&eM-~YKi8Me& zAgQ+~gn?l}J46JcYjy|&1H1%)w-X?F2UKos_F`awoyiaOB)pvf*To3R?`UNU^h8a( zH3+oa0G*l$DK}s@??CJUk+5=uhYeOcfX?d&o#P6UgvNhMNO%kEsWS?V#($P+VxngJC;EH#vTvfnmD?hV7tp zfKhG##sPOT)bAW*+1`U;y9S2s9T*{h2*Y*(vTU!xupQKF-*Yf*4&khm;VQ5bi)9FrwN%31K@l$KAt| z<6xx?!W>v>gJc&p{V?=EM_{m(B%q-t1_lOrdd8-PgwjR>R)66sZ4hpT=)UKyCmy`V)z;5fy--sV&Y1H%P8w<9H%fXroJ*Z?yJK2D1W)sqYi44`{}VD=(b zOi`R#pyRY>SYfkxp!HP)j zV1(RJ#LUaB9pK<-M|gGZ31$O z8L|on_*@BSrWaHa;F^a8#f6s&EH2`p{3nO4@;{ZHuz+@0K zEWg8TX9Tr(5%M4wZ1u<&czy@PDM$u^VfkH#19!axn;(Sx0(=t+EaVVofzsg$O;|eI zzz5#8jgSVjK~!thc*3x$^=jk8d4^J?h(aQ21=`-F)K(~1=UiR?g6D$J9gx> zifIlgtx7X8Fo04ZGb89ISg`)%ia~W#1QV>>0FC8gubURb({o8qadBw@ zYFmea;Swl~^+VDaC=Y?mM#m39sSc_J;_DBg3=9c)Yodyj_(TQ<4+aJXP+88*5FcNW z5?=+mg%ctIvV}d2fk9vb#CF7*w*s(TpnSTR0n&m2sfA!t(kf`B6F8@U(+@;HjD)3C z5jKbo%vjc5!qTb` zf^_3lQ<77x5)~B%s!t(yf!Y!5|Pje!Al3Cia}6x%q{1~`p5gX0~)8qX+DS|y;y5fnDK)PVd4Nrl(%Gg5O3Qj3b=5i1;}moFenDA;4uihJslwr zVu51NfDv-jFTS2;02eF<|1yATDUe=d42r=Ccw!Kw7F1J%X4^rjlbI1zE~ADKES6c> zVdD}7#i^w!`MO1^Ir+)(NM=Gy}nHfql zQsawLlZ!oIITx%Z1yl=90{IV=Hb6pkAiGf1^oB7oG~lW6LAv&ZF)&OZpyoBmg?QA! zQYOTwpfc(t&KshMX|FoKe1WYE0IhRDx5a?k&diLMtH@#P1kk;tkTq|hnjYj^1cs&F z0(k1hY$t$JgHrEzS6J#jDZs#x1`XIwAsSSUfiCZ1W;DQ509uyH#K7Ro4a)`YOrTVP<{WVUD;-|P zC80}$`JkM#fry;)1A1RCGb3nK1Sr*lwSx&*PSJwrl;on4_>9E7lpN5Caj+T?!5PlL zATSl=Qat5hI0M6mX;3vFO$?Ud3=A2t)B+aq2xnkuV29Kap!C7O5CjrIQ4Cy^bvCy%JK&)dZMbj0-Sg2bMB(;N>c)CIUMgPQX$osFeaLb(k43Qzopp zRUiuM9ixs0!3+bX-UDH<)T=29Y2CwQAv7rUiu1v0PX`P)gH(Z9UzrlH)|V!z!UhSW zU{LCvKt%aGg%_4GRdA-vlkk*@x(XF$At+^DAR=Xg?qG(L;h=UQ_LS)dPnjh-#U&X< zDTv}5Y+Z3U14F`0NNT}VhEEP>U{K&hPIU{zA>AmDose|6KAeH!0@NHxAfE|mVBkQ} zbw8YeAz&87E{K}{;S3BP;3BZJ3ULo8t%7PQHLyEj1Sx41)X@XG5M0N>On}g^wAuqt ztC*v*u(Y}ZUW$TBEl~LdF#|?|(&5W&SUP;bfo@3iz|tXTB`Zuff(E6-Ns6#^ zxB}#Kgd~&&N{0ssq(e|C3R+4zm#qRtSo( zlO^!@g6{(bOF{`ye0>*$#g`UT6qN$S#Rqs?fXX^d3qj%REeH$e9#jLMJWx0bSis6U z&^QcK0!qQcum~Q8APHg6@(B%h%lTD55t1g;?!iMF>8of>JbbKkaVcPfU7)o zj9_5+08Xp0)@eut1B1XEQ0j$=Bu6kXB%p{?fZ}T|vYM$83=9iUMAk+yFg!pJIS|3X zP%sZ!*M$fMh7BkpZzC8O0_MZiV5S5w7RZCy$O&F z2n;JlU%*RI(0VV_@m`Q>P>Q-+21`-mpnD)e!YCM&qP*cLDh_MX1eKyz8n9CIFxI96 zDCdG&m!Ps7S8oecih|bVLP}APY80n{IiTE~D+J5k-Z*P~J0{rpDrhPftQCa-rQQHM zsTZUeR2P0=gN?6(W@u1*$gomk8$5+O2Zh9^6&M;Is{Hu0f}+&o)DndS(A0uiU@<_I zFu>KoXXqdz3=GVSkUAgaDj24AiULglLV7otXT)J~`VVKEULYb)Gl+;&9eA8VM(oSd3W_rG zO449aD;vqcFoB3Tg{T3=sUn7(shv-G;BksMpa^RNv=9-e@8-hd^d}Po186=Elp7Ej zluxz9VEJ?#LIQ~eYE}N#hqWr7BFP|_pu7y~cN2>DQxdRv=f~Rb2F=h-g0~n!J!^y= zFcv7ke;^`l%z&p2&?qtXde94=Hp+_gP?H7NI=x5+h66;T4Tu_0+5olOP@9(2P8*;d zVIW)ZmEjYJNE^==z|ux869Z_w2-wF60+co$z|#imd2%pSptSMG0G2kMz=Tm~P}%_P zw<44_Zc4(^2596TH4H%pg3`t!c-r6w31MJR+7PhBJ)Qv40ICZvz|#h1y#uS?tKn$_ zQf?HatrnQ_s}U(gfbq1X<4D4brs`(g%U`C88o37z`F6 zi{wQzFgPqm7HI^DEI}5T9m&A(07YaQ$mFHSYEFZ6twa{N9m&9;unJk^LnH&khc(C| zY*7ph2i78sh(|Fn6s$uQ(THMTVAy~xVi(20Fkv&YNMIBLL&6S-2x2`6B*Z{D1$5_| zGB`|N1ZGYF%{;vo1{U@Jb4Xt$60cL;t5?5D95dZ=eVQj(qKL)#~Bcj<3N=U zs61z8oWR1sfI0>O%W?DIIW8qPvlwZ*6>Jj&Lt+#I!-qp4mtt>yfs_?QF)%0`hN=Mx zFf>FlFf2e3=?0~6xCpEr4lx;&GC`vmsO=wE$^@O#1gc4}<#$q2<{x;<1eHHvXTk|s z$^^}KgIoa_r2)k^TnU%~N_Cq~!BX8%76t}Pvp}h?Od6Kzk}*;x$RJP|{;&|1>ZW2g zIG{6_7nosVl^}aSp#aj2z@StInk|KOf~%K=xmxrho!?{0R{%lbsnH}cvA+J4ue2J0%1f|2WDp)!+@PMq1 zMp6JxhoCb$K)C^uFEEV)r9;r!8jy4dN~@@5fOw#ExJnI{4z*c9u?rGM#-P$~9y}c$ zLzaTGLFur7h;%rEAC?Y5tIAQ`3`>U(;iX?X*3xfN6az!UEs#rbmwx*}weW4I8u&;K zL>DO4`5}@e)DKikbOHrm6{sO54rKn|xU@6KGYb=9Wrv@zeOEyqyxvC9IEx3CwDJ-y33+qHW zcx~YXB2tTj0CH*p)d3*iKrk$|h;zb5DzGLNhBHwN3>Tm&0b6YaGVL0uJ{3Zq-F^&e z*9jwwd;ry+cOWSW(r96dW?=XL6=7mXD@skx$t*5efMO0mNDXYR4P+IAWHbZA0@!>C zSVT3NfkEIN#AHa0FpOqkXh0FMk7i&nxR0#HGn#=R0YxMvnt@@$1DKiuQ}A+Hh6WV7 zlA{?I4#3JouoHmHIIQI;T5tPu^0x1 z1h^VlnuoX7;8WEF@L=@~d-^^Oaw0?^sc z(2;G>*+-z#6w-14wINWIg3`0nPl!vI8JpP{7%)#<1*PW|%CLGT4QIX6`~jAp`8h!$ zg=!O|9|!6Qfm{J`52Or4RtHL}Tt=|8x)tY$Su;GX8X{|lvq5R~0ugDoKm=CrR6tK0 zfE&R8ORE-KuzIIFxu_&RHwmS^%)p=)!@zI=nl`YdRj4iohu5G`fvp)Zj$vS!02hI! zABZ`i^aBcY)G~pT^iu#!nOI^AR%W`u(+_9|1;XDT7Ocz!omK})KbR}`KQX+9)Ds9f-3Cptipt%XSS6+dZIrF%h<} zfbFb@xzMZ*!}bqk*{*_NJLnz^6rY-aR;{9lm=$5z{(~&rL3<}rOg6Q^@cRr5zh_|B z&hVC;c$dNOy8(vnpm}o?7n;Rk*v>(g?GhNaYhc*kf#LTc4BG|BvRwqjb_ERE8!&A5 zz_49{EZaf*YEeSfR06~H3JlxrFl<*K%XS_NzYAd4UVve{35M+&WZBMvVLJzg?HL%h z>tNV!K$h(+7`B5hb3us<6VSRH6cIBe4BIWpvK`cBLowMDw9_9�b-7DdEN0>gF( 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.dylib new file mode 100644 index 0000000000000000000000000000000000000000..f6acabed3d3794697ca937ce325e2600f2836ed8 GIT binary patch literal 106760 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�gRSCiaudC^0 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 literal 0 HcmV?d00001 diff --git a/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so b/Ryujinx.Audio/Native/libsoundio/libs/libsoundio.so new file mode 100644 index 0000000000000000000000000000000000000000..2b9cf1b30e77113a78fecee889b809d408aa6b8d GIT binary patch literal 265888 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 Nj)NY{50^yO4*=>N^2q=I literal 0 HcmV?d00001 diff --git a/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs b/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs new file mode 100644 index 0000000000..6eb09370f1 --- /dev/null +++ b/Ryujinx.Audio/Native/libsoundio/libsoundio-interop.cs @@ -0,0 +1,638 @@ +// This source file is generated by nclang PInvokeGenerator. +using System; +using System.Runtime.InteropServices; +using delegate0 = SoundIOSharp.Delegates.delegate0; +using delegate1 = SoundIOSharp.Delegates.delegate1; +using delegate2 = SoundIOSharp.Delegates.delegate2; +using delegate3 = SoundIOSharp.Delegates.delegate3; +using delegate4 = SoundIOSharp.Delegates.delegate4; +using delegate5 = SoundIOSharp.Delegates.delegate5; +using delegate6 = SoundIOSharp.Delegates.delegate6; +using delegate7 = SoundIOSharp.Delegates.delegate7; +using delegate8 = SoundIOSharp.Delegates.delegate8; +using delegate9 = SoundIOSharp.Delegates.delegate9; + +namespace SoundIOSharp +{ + enum SoundIoError // soundio.h (72, 6) + { + SoundIoErrorNone = 0, + SoundIoErrorNoMem = 1, + SoundIoErrorInitAudioBackend = 2, + SoundIoErrorSystemResources = 3, + SoundIoErrorOpeningDevice = 4, + SoundIoErrorNoSuchDevice = 5, + SoundIoErrorInvalid = 6, + SoundIoErrorBackendUnavailable = 7, + SoundIoErrorStreaming = 8, + SoundIoErrorIncompatibleDevice = 9, + SoundIoErrorNoSuchClient = 10, + SoundIoErrorIncompatibleBackend = 11, + SoundIoErrorBackendDisconnected = 12, + SoundIoErrorInterrupted = 13, + SoundIoErrorUnderflow = 14, + SoundIoErrorEncodingString = 15, + } + + enum SoundIoChannelId // soundio.h (106, 6) + { + SoundIoChannelIdInvalid = 0, + SoundIoChannelIdFrontLeft = 1, + SoundIoChannelIdFrontRight = 2, + SoundIoChannelIdFrontCenter = 3, + SoundIoChannelIdLfe = 4, + SoundIoChannelIdBackLeft = 5, + SoundIoChannelIdBackRight = 6, + SoundIoChannelIdFrontLeftCenter = 7, + SoundIoChannelIdFrontRightCenter = 8, + SoundIoChannelIdBackCenter = 9, + SoundIoChannelIdSideLeft = 10, + SoundIoChannelIdSideRight = 11, + SoundIoChannelIdTopCenter = 12, + SoundIoChannelIdTopFrontLeft = 13, + SoundIoChannelIdTopFrontCenter = 14, + SoundIoChannelIdTopFrontRight = 15, + SoundIoChannelIdTopBackLeft = 16, + SoundIoChannelIdTopBackCenter = 17, + SoundIoChannelIdTopBackRight = 18, + SoundIoChannelIdBackLeftCenter = 19, + SoundIoChannelIdBackRightCenter = 20, + SoundIoChannelIdFrontLeftWide = 21, + SoundIoChannelIdFrontRightWide = 22, + SoundIoChannelIdFrontLeftHigh = 23, + SoundIoChannelIdFrontCenterHigh = 24, + SoundIoChannelIdFrontRightHigh = 25, + SoundIoChannelIdTopFrontLeftCenter = 26, + SoundIoChannelIdTopFrontRightCenter = 27, + SoundIoChannelIdTopSideLeft = 28, + SoundIoChannelIdTopSideRight = 29, + SoundIoChannelIdLeftLfe = 30, + SoundIoChannelIdRightLfe = 31, + SoundIoChannelIdLfe2 = 32, + SoundIoChannelIdBottomCenter = 33, + SoundIoChannelIdBottomLeftCenter = 34, + SoundIoChannelIdBottomRightCenter = 35, + SoundIoChannelIdMsMid = 36, + SoundIoChannelIdMsSide = 37, + SoundIoChannelIdAmbisonicW = 38, + SoundIoChannelIdAmbisonicX = 39, + SoundIoChannelIdAmbisonicY = 40, + SoundIoChannelIdAmbisonicZ = 41, + SoundIoChannelIdXyX = 42, + SoundIoChannelIdXyY = 43, + SoundIoChannelIdHeadphonesLeft = 44, + SoundIoChannelIdHeadphonesRight = 45, + SoundIoChannelIdClickTrack = 46, + SoundIoChannelIdForeignLanguage = 47, + SoundIoChannelIdHearingImpaired = 48, + SoundIoChannelIdNarration = 49, + SoundIoChannelIdHaptic = 50, + SoundIoChannelIdDialogCentricMix = 51, + SoundIoChannelIdAux = 52, + SoundIoChannelIdAux0 = 53, + SoundIoChannelIdAux1 = 54, + SoundIoChannelIdAux2 = 55, + SoundIoChannelIdAux3 = 56, + SoundIoChannelIdAux4 = 57, + SoundIoChannelIdAux5 = 58, + SoundIoChannelIdAux6 = 59, + SoundIoChannelIdAux7 = 60, + SoundIoChannelIdAux8 = 61, + SoundIoChannelIdAux9 = 62, + SoundIoChannelIdAux10 = 63, + SoundIoChannelIdAux11 = 64, + SoundIoChannelIdAux12 = 65, + SoundIoChannelIdAux13 = 66, + SoundIoChannelIdAux14 = 67, + SoundIoChannelIdAux15 = 68, + } + + enum SoundIoChannelLayoutId // soundio.h (189, 6) + { + SoundIoChannelLayoutIdMono = 0, + SoundIoChannelLayoutIdStereo = 1, + SoundIoChannelLayoutId2Point1 = 2, + SoundIoChannelLayoutId3Point0 = 3, + SoundIoChannelLayoutId3Point0Back = 4, + SoundIoChannelLayoutId3Point1 = 5, + SoundIoChannelLayoutId4Point0 = 6, + SoundIoChannelLayoutIdQuad = 7, + SoundIoChannelLayoutIdQuadSide = 8, + SoundIoChannelLayoutId4Point1 = 9, + SoundIoChannelLayoutId5Point0Back = 10, + SoundIoChannelLayoutId5Point0Side = 11, + SoundIoChannelLayoutId5Point1 = 12, + SoundIoChannelLayoutId5Point1Back = 13, + SoundIoChannelLayoutId6Point0Side = 14, + SoundIoChannelLayoutId6Point0Front = 15, + SoundIoChannelLayoutIdHexagonal = 16, + SoundIoChannelLayoutId6Point1 = 17, + SoundIoChannelLayoutId6Point1Back = 18, + SoundIoChannelLayoutId6Point1Front = 19, + SoundIoChannelLayoutId7Point0 = 20, + SoundIoChannelLayoutId7Point0Front = 21, + SoundIoChannelLayoutId7Point1 = 22, + SoundIoChannelLayoutId7Point1Wide = 23, + SoundIoChannelLayoutId7Point1WideBack = 24, + SoundIoChannelLayoutIdOctagonal = 25, + } + + enum SoundIoBackend // soundio.h (218, 6) + { + SoundIoBackendNone = 0, + SoundIoBackendJack = 1, + SoundIoBackendPulseAudio = 2, + SoundIoBackendAlsa = 3, + SoundIoBackendCoreAudio = 4, + SoundIoBackendWasapi = 5, + SoundIoBackendDummy = 6, + } + + enum SoundIoDeviceAim // soundio.h (228, 6) + { + SoundIoDeviceAimInput = 0, + SoundIoDeviceAimOutput = 1, + } + + enum SoundIoFormat // soundio.h (235, 6) + { + SoundIoFormatInvalid = 0, + SoundIoFormatS8 = 1, + SoundIoFormatU8 = 2, + SoundIoFormatS16LE = 3, + SoundIoFormatS16BE = 4, + SoundIoFormatU16LE = 5, + SoundIoFormatU16BE = 6, + SoundIoFormatS24LE = 7, + SoundIoFormatS24BE = 8, + SoundIoFormatU24LE = 9, + SoundIoFormatU24BE = 10, + SoundIoFormatS32LE = 11, + SoundIoFormatS32BE = 12, + SoundIoFormatU32LE = 13, + SoundIoFormatU32BE = 14, + SoundIoFormatFloat32LE = 15, + SoundIoFormatFloat32BE = 16, + SoundIoFormatFloat64LE = 17, + SoundIoFormatFloat64BE = 18, + } + + [StructLayout(LayoutKind.Sequential)] + struct SoundIoChannelLayout // soundio.h (302, 8) + { + [CTypeDetails("Pointer")] public System.IntPtr @name; + public int @channel_count; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + [CTypeDetails("ConstArrayOf")] public SoundIoChannelId[] @channels; + } + + [StructLayout(LayoutKind.Sequential)] + struct SoundIoSampleRateRange // soundio.h (309, 8) + { + public int @min; + public int @max; + } + + [StructLayout(LayoutKind.Sequential)] + struct SoundIoChannelArea // soundio.h (315, 8) + { + [CTypeDetails("Pointer")] public System.IntPtr @ptr; + public int @step; + } + + [StructLayout(LayoutKind.Sequential)] + struct SoundIo // soundio.h (324, 8) + { + [CTypeDetails("Pointer")] public System.IntPtr @userdata; + [CTypeDetails("Pointer")] public delegate0 @on_devices_change; + [CTypeDetails("Pointer")] public delegate1 @on_backend_disconnect; + [CTypeDetails("Pointer")] public Delegates.delegate0 @on_events_signal; + public SoundIoBackend @current_backend; + [CTypeDetails("Pointer")] public System.IntPtr @app_name; + [CTypeDetails("Pointer")] public delegate2 @emit_rtprio_warning; + [CTypeDetails("Pointer")] public delegate3 @jack_info_callback; + [CTypeDetails("Pointer")] public Delegates.delegate3 @jack_error_callback; + } + + [StructLayout(LayoutKind.Sequential)] + struct SoundIoDevice // soundio.h (383, 8) + { + [CTypeDetails("Pointer")] public System.IntPtr @soundio; + [CTypeDetails("Pointer")] public System.IntPtr @id; + [CTypeDetails("Pointer")] public System.IntPtr @name; + public SoundIoDeviceAim @aim; + [CTypeDetails("Pointer")] public System.IntPtr @layouts; + public int @layout_count; + public SoundIoChannelLayout @current_layout; + [CTypeDetails("Pointer")] public System.IntPtr @formats; + public int @format_count; + public SoundIoFormat @current_format; + [CTypeDetails("Pointer")] public System.IntPtr @sample_rates; + public int @sample_rate_count; + public int @sample_rate_current; + public double @software_latency_min; + public double @software_latency_max; + public double @software_latency_current; + public bool @is_raw; + public int @ref_count; + public int @probe_error; + } + + [StructLayout(LayoutKind.Sequential)] + struct SoundIoOutStream // soundio.h (493, 8) + { + [CTypeDetails("Pointer")] public System.IntPtr @device; + public SoundIoFormat @format; + public int @sample_rate; + public SoundIoChannelLayout @layout; + public double @software_latency; + [CTypeDetails("Pointer")] public System.IntPtr @userdata; + [CTypeDetails("Pointer")] public delegate4 @write_callback; + [CTypeDetails("Pointer")] public delegate5 @underflow_callback; + [CTypeDetails("Pointer")] public delegate6 @error_callback; + [CTypeDetails("Pointer")] public System.IntPtr @name; + public bool @non_terminal_hint; + public int @bytes_per_frame; + public int @bytes_per_sample; + public int @layout_error; + } + + [StructLayout(LayoutKind.Sequential)] + struct SoundIoInStream // soundio.h (595, 8) + { + [CTypeDetails("Pointer")] public System.IntPtr @device; + public SoundIoFormat @format; + public int @sample_rate; + public SoundIoChannelLayout @layout; + public double @software_latency; + [CTypeDetails("Pointer")] public System.IntPtr @userdata; + [CTypeDetails("Pointer")] public delegate7 @read_callback; + [CTypeDetails("Pointer")] public delegate8 @overflow_callback; + [CTypeDetails("Pointer")] public delegate9 @error_callback; + [CTypeDetails("Pointer")] public System.IntPtr @name; + public bool @non_terminal_hint; + public int @bytes_per_frame; + public int @bytes_per_sample; + public int @layout_error; + } + + [StructLayout(LayoutKind.Sequential)] + struct SoundIoRingBuffer // soundio.h (1167, 8) + { + } + + partial class Natives + { + const string LibraryName = "libsoundio"; + // function soundio_version_string - soundio.h (677, 28) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_version_string(); + + // function soundio_version_major - soundio.h (679, 20) + [DllImport(LibraryName)] + internal static extern int soundio_version_major(); + + // function soundio_version_minor - soundio.h (681, 20) + [DllImport(LibraryName)] + internal static extern int soundio_version_minor(); + + // function soundio_version_patch - soundio.h (683, 20) + [DllImport(LibraryName)] + internal static extern int soundio_version_patch(); + + // function soundio_create - soundio.h (689, 32) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_create(); + + // function soundio_destroy - soundio.h (690, 21) + [DllImport(LibraryName)] + internal static extern void soundio_destroy([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_connect - soundio.h (700, 20) + [DllImport(LibraryName)] + internal static extern int soundio_connect([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_connect_backend - soundio.h (712, 20) + [DllImport(LibraryName)] + internal static extern int soundio_connect_backend([CTypeDetails("Pointer")]System.IntPtr @soundio, SoundIoBackend @backend); + + // function soundio_disconnect - soundio.h (713, 21) + [DllImport(LibraryName)] + internal static extern void soundio_disconnect([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_strerror - soundio.h (716, 28) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_strerror(int @error); + + // function soundio_backend_name - soundio.h (718, 28) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_backend_name(SoundIoBackend @backend); + + // function soundio_backend_count - soundio.h (721, 20) + [DllImport(LibraryName)] + internal static extern int soundio_backend_count([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_get_backend - soundio.h (724, 36) + [DllImport(LibraryName)] + internal static extern SoundIoBackend soundio_get_backend([CTypeDetails("Pointer")]System.IntPtr @soundio, int @index); + + // function soundio_have_backend - soundio.h (727, 21) + [DllImport(LibraryName)] + internal static extern bool soundio_have_backend(SoundIoBackend @backend); + + // function soundio_flush_events - soundio.h (751, 21) + [DllImport(LibraryName)] + internal static extern void soundio_flush_events([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_wait_events - soundio.h (755, 21) + [DllImport(LibraryName)] + internal static extern void soundio_wait_events([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_wakeup - soundio.h (758, 21) + [DllImport(LibraryName)] + internal static extern void soundio_wakeup([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_force_device_scan - soundio.h (775, 21) + [DllImport(LibraryName)] + internal static extern void soundio_force_device_scan([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_channel_layout_equal - soundio.h (782, 21) + [DllImport(LibraryName)] + internal static extern bool soundio_channel_layout_equal([CTypeDetails("Pointer")]System.IntPtr @a, [CTypeDetails("Pointer")]System.IntPtr @b); + + // function soundio_get_channel_name - soundio.h (786, 28) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_get_channel_name(SoundIoChannelId @id); + + // function soundio_parse_channel_id - soundio.h (790, 38) + [DllImport(LibraryName)] + internal static extern SoundIoChannelId soundio_parse_channel_id([CTypeDetails("Pointer")]System.IntPtr @str, int @str_len); + + // function soundio_channel_layout_builtin_count - soundio.h (793, 20) + [DllImport(LibraryName)] + internal static extern int soundio_channel_layout_builtin_count(); + + // function soundio_channel_layout_get_builtin - soundio.h (798, 51) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_channel_layout_get_builtin(int @index); + + // function soundio_channel_layout_get_default - soundio.h (801, 51) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_channel_layout_get_default(int @channel_count); + + // function soundio_channel_layout_find_channel - soundio.h (804, 20) + [DllImport(LibraryName)] + internal static extern int soundio_channel_layout_find_channel([CTypeDetails("Pointer")]System.IntPtr @layout, SoundIoChannelId @channel); + + // function soundio_channel_layout_detect_builtin - soundio.h (809, 21) + [DllImport(LibraryName)] + internal static extern bool soundio_channel_layout_detect_builtin([CTypeDetails("Pointer")]System.IntPtr @layout); + + // function soundio_best_matching_channel_layout - soundio.h (814, 51) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_best_matching_channel_layout([CTypeDetails("Pointer")]System.IntPtr @preferred_layouts, int @preferred_layout_count, [CTypeDetails("Pointer")]System.IntPtr @available_layouts, int @available_layout_count); + + // function soundio_sort_channel_layouts - soundio.h (819, 21) + [DllImport(LibraryName)] + internal static extern void soundio_sort_channel_layouts([CTypeDetails("Pointer")]System.IntPtr @layouts, int @layout_count); + + // function soundio_get_bytes_per_sample - soundio.h (825, 20) + [DllImport(LibraryName)] + internal static extern int soundio_get_bytes_per_sample(SoundIoFormat @format); + + // function soundio_get_bytes_per_frame - soundio.h (828, 19) + [DllImport(LibraryName)] + internal static extern int soundio_get_bytes_per_frame(SoundIoFormat @format, int @channel_count); + + // function soundio_get_bytes_per_second - soundio.h (833, 19) + [DllImport(LibraryName)] + internal static extern int soundio_get_bytes_per_second(SoundIoFormat @format, int @channel_count, int @sample_rate); + + // function soundio_format_string - soundio.h (840, 29) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_format_string(SoundIoFormat @format); + + // function soundio_input_device_count - soundio.h (856, 20) + [DllImport(LibraryName)] + internal static extern int soundio_input_device_count([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_output_device_count - soundio.h (859, 20) + [DllImport(LibraryName)] + internal static extern int soundio_output_device_count([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_get_input_device - soundio.h (865, 38) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_get_input_device([CTypeDetails("Pointer")]System.IntPtr @soundio, int @index); + + // function soundio_get_output_device - soundio.h (870, 38) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_get_output_device([CTypeDetails("Pointer")]System.IntPtr @soundio, int @index); + + // function soundio_default_input_device_index - soundio.h (875, 20) + [DllImport(LibraryName)] + internal static extern int soundio_default_input_device_index([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_default_output_device_index - soundio.h (880, 20) + [DllImport(LibraryName)] + internal static extern int soundio_default_output_device_index([CTypeDetails("Pointer")]System.IntPtr @soundio); + + // function soundio_device_ref - soundio.h (883, 21) + [DllImport(LibraryName)] + internal static extern void soundio_device_ref([CTypeDetails("Pointer")]System.IntPtr @device); + + // function soundio_device_unref - soundio.h (886, 21) + [DllImport(LibraryName)] + internal static extern void soundio_device_unref([CTypeDetails("Pointer")]System.IntPtr @device); + + // function soundio_device_equal - soundio.h (890, 21) + [DllImport(LibraryName)] + internal static extern bool soundio_device_equal([CTypeDetails("Pointer")]System.IntPtr @a, [CTypeDetails("Pointer")]System.IntPtr @b); + + // function soundio_device_sort_channel_layouts - soundio.h (895, 21) + [DllImport(LibraryName)] + internal static extern void soundio_device_sort_channel_layouts([CTypeDetails("Pointer")]System.IntPtr @device); + + // function soundio_device_supports_format - soundio.h (899, 21) + [DllImport(LibraryName)] + internal static extern bool soundio_device_supports_format([CTypeDetails("Pointer")]System.IntPtr @device, SoundIoFormat @format); + + // function soundio_device_supports_layout - soundio.h (904, 21) + [DllImport(LibraryName)] + internal static extern bool soundio_device_supports_layout([CTypeDetails("Pointer")]System.IntPtr @device, [CTypeDetails("Pointer")]System.IntPtr @layout); + + // function soundio_device_supports_sample_rate - soundio.h (909, 21) + [DllImport(LibraryName)] + internal static extern bool soundio_device_supports_sample_rate([CTypeDetails("Pointer")]System.IntPtr @device, int @sample_rate); + + // function soundio_device_nearest_sample_rate - soundio.h (914, 20) + [DllImport(LibraryName)] + internal static extern int soundio_device_nearest_sample_rate([CTypeDetails("Pointer")]System.IntPtr @device, int @sample_rate); + + // function soundio_outstream_create - soundio.h (924, 41) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_outstream_create([CTypeDetails("Pointer")]System.IntPtr @device); + + // function soundio_outstream_destroy - soundio.h (926, 21) + [DllImport(LibraryName)] + internal static extern void soundio_outstream_destroy([CTypeDetails("Pointer")]System.IntPtr @outstream); + + // function soundio_outstream_open - soundio.h (950, 20) + [DllImport(LibraryName)] + internal static extern int soundio_outstream_open([CTypeDetails("Pointer")]System.IntPtr @outstream); + + // function soundio_outstream_start - soundio.h (961, 20) + [DllImport(LibraryName)] + internal static extern int soundio_outstream_start([CTypeDetails("Pointer")]System.IntPtr @outstream); + + // function soundio_outstream_begin_write - soundio.h (993, 20) + [DllImport(LibraryName)] + internal static extern int soundio_outstream_begin_write([CTypeDetails("Pointer")]System.IntPtr @outstream, [CTypeDetails("Pointer")]System.IntPtr @areas, [CTypeDetails("Pointer")]System.IntPtr @frame_count); + + // function soundio_outstream_end_write - soundio.h (1005, 20) + [DllImport(LibraryName)] + internal static extern int soundio_outstream_end_write([CTypeDetails("Pointer")]System.IntPtr @outstream); + + // function soundio_outstream_clear_buffer - soundio.h (1020, 20) + [DllImport(LibraryName)] + internal static extern int soundio_outstream_clear_buffer([CTypeDetails("Pointer")]System.IntPtr @outstream); + + // function soundio_outstream_pause - soundio.h (1041, 20) + [DllImport(LibraryName)] + internal static extern int soundio_outstream_pause([CTypeDetails("Pointer")]System.IntPtr @outstream, bool @pause); + + // function soundio_outstream_get_latency - soundio.h (1054, 20) + [DllImport(LibraryName)] + internal static extern int soundio_outstream_get_latency([CTypeDetails("Pointer")]System.IntPtr @outstream, [CTypeDetails("Pointer")]System.IntPtr @out_latency); + + // function soundio_instream_create - soundio.h (1064, 40) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_instream_create([CTypeDetails("Pointer")]System.IntPtr @device); + + // function soundio_instream_destroy - soundio.h (1066, 21) + [DllImport(LibraryName)] + internal static extern void soundio_instream_destroy([CTypeDetails("Pointer")]System.IntPtr @instream); + + // function soundio_instream_open - soundio.h (1086, 20) + [DllImport(LibraryName)] + internal static extern int soundio_instream_open([CTypeDetails("Pointer")]System.IntPtr @instream); + + // function soundio_instream_start - soundio.h (1095, 20) + [DllImport(LibraryName)] + internal static extern int soundio_instream_start([CTypeDetails("Pointer")]System.IntPtr @instream); + + // function soundio_instream_begin_read - soundio.h (1126, 20) + [DllImport(LibraryName)] + internal static extern int soundio_instream_begin_read([CTypeDetails("Pointer")]System.IntPtr @instream, [CTypeDetails("Pointer")]System.IntPtr @areas, [CTypeDetails("Pointer")]System.IntPtr @frame_count); + + // function soundio_instream_end_read - soundio.h (1136, 20) + [DllImport(LibraryName)] + internal static extern int soundio_instream_end_read([CTypeDetails("Pointer")]System.IntPtr @instream); + + // function soundio_instream_pause - soundio.h (1149, 20) + [DllImport(LibraryName)] + internal static extern int soundio_instream_pause([CTypeDetails("Pointer")]System.IntPtr @instream, bool @pause); + + // function soundio_instream_get_latency - soundio.h (1159, 20) + [DllImport(LibraryName)] + internal static extern int soundio_instream_get_latency([CTypeDetails("Pointer")]System.IntPtr @instream, [CTypeDetails("Pointer")]System.IntPtr @out_latency); + + // function soundio_ring_buffer_create - soundio.h (1173, 42) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_ring_buffer_create([CTypeDetails("Pointer")]System.IntPtr @soundio, int @requested_capacity); + + // function soundio_ring_buffer_destroy - soundio.h (1174, 21) + [DllImport(LibraryName)] + internal static extern void soundio_ring_buffer_destroy([CTypeDetails("Pointer")]System.IntPtr @ring_buffer); + + // function soundio_ring_buffer_capacity - soundio.h (1178, 20) + [DllImport(LibraryName)] + internal static extern int soundio_ring_buffer_capacity([CTypeDetails("Pointer")]System.IntPtr @ring_buffer); + + // function soundio_ring_buffer_write_ptr - soundio.h (1181, 22) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_ring_buffer_write_ptr([CTypeDetails("Pointer")]System.IntPtr @ring_buffer); + + // function soundio_ring_buffer_advance_write_ptr - soundio.h (1183, 21) + [DllImport(LibraryName)] + internal static extern void soundio_ring_buffer_advance_write_ptr([CTypeDetails("Pointer")]System.IntPtr @ring_buffer, int @count); + + // function soundio_ring_buffer_read_ptr - soundio.h (1186, 22) + [DllImport(LibraryName)] + internal static extern System.IntPtr soundio_ring_buffer_read_ptr([CTypeDetails("Pointer")]System.IntPtr @ring_buffer); + + // function soundio_ring_buffer_advance_read_ptr - soundio.h (1188, 21) + [DllImport(LibraryName)] + internal static extern void soundio_ring_buffer_advance_read_ptr([CTypeDetails("Pointer")]System.IntPtr @ring_buffer, int @count); + + // function soundio_ring_buffer_fill_count - soundio.h (1191, 20) + [DllImport(LibraryName)] + internal static extern int soundio_ring_buffer_fill_count([CTypeDetails("Pointer")]System.IntPtr @ring_buffer); + + // function soundio_ring_buffer_free_count - soundio.h (1194, 20) + [DllImport(LibraryName)] + internal static extern int soundio_ring_buffer_free_count([CTypeDetails("Pointer")]System.IntPtr @ring_buffer); + + // function soundio_ring_buffer_clear - soundio.h (1197, 21) + [DllImport(LibraryName)] + internal static extern void soundio_ring_buffer_clear([CTypeDetails("Pointer")]System.IntPtr @ring_buffer); + + } + + class Delegates + { + public delegate void delegate0(System.IntPtr p0); + public delegate void delegate1(System.IntPtr p0, int p1); + public delegate void delegate2(); + public delegate void delegate3(System.IntPtr p0); + public delegate void delegate4(System.IntPtr p0, int p1, int p2); + public delegate void delegate5(System.IntPtr p0); + public delegate void delegate6(System.IntPtr p0, int p1); + public delegate void delegate7(System.IntPtr p0, int p1, int p2); + public delegate void delegate8(System.IntPtr p0); + public delegate void delegate9(System.IntPtr p0, int p1); + } + + public struct Pointer + { + public IntPtr Handle; + public static implicit operator IntPtr(Pointer value) { return value.Handle; } + public static implicit operator Pointer(IntPtr value) { return new Pointer(value); } + + public Pointer(IntPtr handle) + { + Handle = handle; + } + + public override bool Equals(object obj) + { + return obj is Pointer && this == (Pointer)obj; + } + + public override int GetHashCode() + { + return (int)Handle; + } + + public static bool operator ==(Pointer p1, Pointer p2) + { + return p1.Handle == p2.Handle; + } + + public static bool operator !=(Pointer p1, Pointer p2) + { + return p1.Handle != p2.Handle; + } + } + public struct ArrayOf { } + public struct ConstArrayOf { } + public class CTypeDetailsAttribute : Attribute + { + public CTypeDetailsAttribute(string value) + { + Value = value; + } + + public string Value { get; set; } + } + +} diff --git a/Ryujinx.Audio/PlaybackState.cs b/Ryujinx.Audio/PlaybackState.cs index 8b53128aaf..7d8620924b 100644 --- a/Ryujinx.Audio/PlaybackState.cs +++ b/Ryujinx.Audio/PlaybackState.cs @@ -1,8 +1,17 @@ namespace Ryujinx.Audio { + /// + /// The playback state of a track + /// public enum PlaybackState { + /// + /// The track is currently playing + /// Playing = 0, + /// + /// The track is currently stopped + /// Stopped = 1 } } \ No newline at end of file diff --git a/Ryujinx.Audio/Renderers/DummyAudioOut.cs b/Ryujinx.Audio/Renderers/DummyAudioOut.cs new file mode 100644 index 0000000000..659734b6dc --- /dev/null +++ b/Ryujinx.Audio/Renderers/DummyAudioOut.cs @@ -0,0 +1,63 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Ryujinx.Audio +{ + /// + /// A Dummy audio renderer that does not output any audio + /// + public class DummyAudioOut : IAalOutput + { + private ConcurrentQueue m_Buffers; + + public DummyAudioOut() + { + m_Buffers = new ConcurrentQueue(); + } + + /// + /// Dummy audio output is always available, Baka! + /// + public static bool IsSupported => true; + + public PlaybackState GetState(int trackId) => PlaybackState.Stopped; + + public int OpenTrack(int sampleRate, int channels, ReleaseCallback callback) => 1; + + public void CloseTrack(int trackId) { } + + public void Start(int trackId) { } + + public void Stop(int trackId) { } + + public void AppendBuffer(int trackID, long bufferTag, T[] buffer) + where T : struct + { + m_Buffers.Enqueue(bufferTag); + } + + public long[] GetReleasedBuffers(int trackId, int maxCount) + { + List bufferTags = new List(); + + for (int i = 0; i < maxCount; i++) + { + if (!m_Buffers.TryDequeue(out long tag)) + { + break; + } + + bufferTags.Add(tag); + } + + return bufferTags.ToArray(); + } + + public bool ContainsBuffer(int trackID, long bufferTag) => false; + + public void Dispose() + { + m_Buffers.Clear(); + } + } +} diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs similarity index 72% rename from Ryujinx.Audio/OpenAL/OpenALAudioOut.cs rename to Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs index 80a070c9f3..93f9287910 100644 --- a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs +++ b/Ryujinx.Audio/Renderers/OpenAL/OpenALAudioOut.cs @@ -6,8 +6,11 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; -namespace Ryujinx.Audio.OpenAL +namespace Ryujinx.Audio { + /// + /// An audio renderer that uses OpenAL as the audio backend + /// public class OpenALAudioOut : IAalOutput, IDisposable { private const int MaxTracks = 256; @@ -28,16 +31,12 @@ namespace Ryujinx.Audio.OpenAL public PlaybackState State { get; set; } - private bool ShouldCallReleaseCallback; - private ConcurrentDictionary Buffers; private Queue QueuedTagsQueue; private Queue ReleasedTagsQueue; - private int LastReleasedCount; - private bool Disposed; public Track(int SampleRate, ALFormat Format, ReleaseCallback Callback) @@ -59,8 +58,6 @@ namespace Ryujinx.Audio.OpenAL public bool ContainsBuffer(long Tag) { - SyncQueuedTags(); - foreach (long QueuedTag in QueuedTagsQueue) { if (QueuedTag == Tag) @@ -72,20 +69,29 @@ namespace Ryujinx.Audio.OpenAL return false; } - public long[] GetReleasedBuffers(int MaxCount) + public long[] GetReleasedBuffers(int Count) { - ClearReleased(); + AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount); + + ReleasedCount += ReleasedTagsQueue.Count; + + if (Count > ReleasedCount) + { + Count = ReleasedCount; + } List Tags = new List(); - HashSet Unique = new HashSet(); - - while (MaxCount-- > 0 && ReleasedTagsQueue.TryDequeue(out long Tag)) + while (Count-- > 0 && ReleasedTagsQueue.TryDequeue(out long Tag)) { - if (Unique.Add(Tag)) - { - Tags.Add(Tag); - } + Tags.Add(Tag); + } + + while (Count-- > 0 && QueuedTagsQueue.TryDequeue(out long Tag)) + { + AL.SourceUnqueueBuffers(SourceId, 1); + + Tags.Add(Tag); } return Tags.ToArray(); @@ -112,67 +118,27 @@ namespace Ryujinx.Audio.OpenAL return Id; } - public void ClearReleased() + public void CallReleaseCallbackIfNeeded() { - SyncQueuedTags(); - AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount); - CheckReleaseChanges(ReleasedCount); - if (ReleasedCount > 0) { - AL.SourceUnqueueBuffers(SourceId, ReleasedCount); - } - } + //If we signal, then we also need to have released buffers available + //to return when GetReleasedBuffers is called. + //If playback needs to be re-started due to all buffers being processed, + //then OpenAL zeros the counts (ReleasedCount), so we keep it on the queue. + while (ReleasedCount-- > 0 && QueuedTagsQueue.TryDequeue(out long Tag)) + { + AL.SourceUnqueueBuffers(SourceId, 1); - public void CallReleaseCallbackIfNeeded() - { - CheckReleaseChanges(); - - if (ShouldCallReleaseCallback) - { - ShouldCallReleaseCallback = false; + ReleasedTagsQueue.Enqueue(Tag); + } Callback(); } } - private void CheckReleaseChanges() - { - AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount); - - CheckReleaseChanges(ReleasedCount); - } - - private void CheckReleaseChanges(int NewReleasedCount) - { - if (LastReleasedCount != NewReleasedCount) - { - LastReleasedCount = NewReleasedCount; - - ShouldCallReleaseCallback = true; - } - } - - private void SyncQueuedTags() - { - AL.GetSource(SourceId, ALGetSourcei.BuffersQueued, out int QueuedCount); - AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount); - - QueuedCount -= ReleasedCount; - - while (QueuedTagsQueue.Count > QueuedCount) - { - ReleasedTagsQueue.Enqueue(QueuedTagsQueue.Dequeue()); - } - - while (ReleasedTagsQueue.Count > MaxReleased) - { - ReleasedTagsQueue.Dequeue(); - } - } - public void Dispose() { Dispose(true); @@ -213,13 +179,34 @@ namespace Ryujinx.Audio.OpenAL AudioPollerThread.Start(); } + /// + /// True if OpenAL is supported on the device. + /// + public static bool IsSupported + { + get + { + try + { + return AudioContext.AvailableDevices.Count > 0; + } + catch + { + return false; + } + } + } + private void AudioPollerWork() { do { foreach (Track Td in Tracks.Values) { - Td.CallReleaseCallbackIfNeeded(); + lock (Td) + { + Td.CallReleaseCallbackIfNeeded(); + } } //If it's not slept it will waste cycles. @@ -266,7 +253,10 @@ namespace Ryujinx.Audio.OpenAL { if (Tracks.TryRemove(Track, out Track Td)) { - Td.Dispose(); + lock (Td) + { + Td.Dispose(); + } } } @@ -274,7 +264,10 @@ namespace Ryujinx.Audio.OpenAL { if (Tracks.TryGetValue(Track, out Track Td)) { - return Td.ContainsBuffer(Tag); + lock (Td) + { + return Td.ContainsBuffer(Tag); + } } return false; @@ -284,7 +277,10 @@ namespace Ryujinx.Audio.OpenAL { if (Tracks.TryGetValue(Track, out Track Td)) { - return Td.GetReleasedBuffers(MaxCount); + lock (Td) + { + return Td.GetReleasedBuffers(MaxCount); + } } return null; @@ -294,15 +290,18 @@ namespace Ryujinx.Audio.OpenAL { if (Tracks.TryGetValue(Track, out Track Td)) { - int BufferId = Td.AppendBuffer(Tag); + lock (Td) + { + int BufferId = Td.AppendBuffer(Tag); - int Size = Buffer.Length * Marshal.SizeOf(); + int Size = Buffer.Length * Marshal.SizeOf(); - AL.BufferData(BufferId, Td.Format, Buffer, Size, Td.SampleRate); + AL.BufferData(BufferId, Td.Format, Buffer, Size, Td.SampleRate); - AL.SourceQueueBuffer(Td.SourceId, BufferId); + AL.SourceQueueBuffer(Td.SourceId, BufferId); - StartPlaybackIfNeeded(Td); + StartPlaybackIfNeeded(Td); + } } } @@ -310,9 +309,12 @@ namespace Ryujinx.Audio.OpenAL { if (Tracks.TryGetValue(Track, out Track Td)) { - Td.State = PlaybackState.Playing; + lock (Td) + { + Td.State = PlaybackState.Playing; - StartPlaybackIfNeeded(Td); + StartPlaybackIfNeeded(Td); + } } } @@ -324,8 +326,6 @@ namespace Ryujinx.Audio.OpenAL if (State != ALSourceState.Playing && Td.State == PlaybackState.Playing) { - Td.ClearReleased(); - AL.SourcePlay(Td.SourceId); } } @@ -334,9 +334,12 @@ namespace Ryujinx.Audio.OpenAL { if (Tracks.TryGetValue(Track, out Track Td)) { - Td.State = PlaybackState.Stopped; + lock (Td) + { + Td.State = PlaybackState.Stopped; - AL.SourceStop(Td.SourceId); + AL.SourceStop(Td.SourceId); + } } } diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs new file mode 100644 index 0000000000..0d3e74ddb9 --- /dev/null +++ b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioOut.cs @@ -0,0 +1,248 @@ +using Ryujinx.Audio.SoundIo; +using SoundIOSharp; +using System.Collections.Generic; + +namespace Ryujinx.Audio +{ + /// + /// An audio renderer that uses libsoundio as the audio backend + /// + public class SoundIoAudioOut : IAalOutput + { + /// + /// The maximum amount of tracks we can issue simultaneously + /// + private const int MaximumTracks = 256; + + /// + /// The audio context + /// + private SoundIO m_AudioContext; + + /// + /// The audio device + /// + private SoundIODevice m_AudioDevice; + + /// + /// An object pool containing objects + /// + private SoundIoAudioTrackPool m_TrackPool; + + /// + /// True if SoundIO is supported on the device. + /// + public static bool IsSupported + { + get + { + SoundIO context = null; + SoundIODevice device = null; + SoundIOOutStream stream = null; + + bool backendDisconnected = false; + + try + { + context = new SoundIO(); + + context.OnBackendDisconnect = (i) => { + backendDisconnected = true; + }; + + context.Connect(); + context.FlushEvents(); + + if(backendDisconnected) + { + return false; + } + + device = context.GetOutputDevice(context.DefaultOutputDeviceIndex); + + if(device == null || backendDisconnected) + { + return false; + } + + stream = device.CreateOutStream(); + + if(stream == null || backendDisconnected) + { + return false; + } + + return true; + } + catch + { + return false; + } + finally + { + if(stream != null) + { + stream.Dispose(); + } + + if(context != null) + { + context.Dispose(); + } + } + } + } + + /// + /// Constructs a new instance of a + /// + public SoundIoAudioOut() + { + m_AudioContext = new SoundIO(); + + m_AudioContext.Connect(); + m_AudioContext.FlushEvents(); + + m_AudioDevice = m_AudioContext.GetOutputDevice(m_AudioContext.DefaultOutputDeviceIndex); + m_TrackPool = new SoundIoAudioTrackPool(m_AudioContext, m_AudioDevice, MaximumTracks); + } + + /// + /// Gets the current playback state of the specified track + /// + /// The track to retrieve the playback state for + public PlaybackState GetState(int trackId) + { + if (m_TrackPool.TryGet(trackId, out SoundIoAudioTrack track)) + { + return track.State; + } + + return PlaybackState.Stopped; + } + + /// + /// Creates a new audio track with the specified parameters + /// + /// The requested sample rate + /// The requested channels + /// A that represents the delegate to invoke when a buffer has been released by the audio track + /// The created track's Track ID + public int OpenTrack(int sampleRate, int channels, ReleaseCallback callback) + { + if (!m_TrackPool.TryGet(out SoundIoAudioTrack track)) + { + return -1; + } + + // Open the output. We currently only support 16-bit signed LE + track.Open(sampleRate, channels, callback, SoundIOFormat.S16LE); + + return track.TrackID; + } + + /// + /// Stops playback and closes the track specified by + /// + /// The ID of the track to close + public void CloseTrack(int trackId) + { + if (m_TrackPool.TryGet(trackId, out SoundIoAudioTrack track)) + { + // Close and dispose of the track + track.Close(); + + // Recycle the track back into the pool + m_TrackPool.Put(track); + } + } + + /// + /// Starts playback + /// + /// The ID of the track to start playback on + public void Start(int trackId) + { + if (m_TrackPool.TryGet(trackId, out SoundIoAudioTrack track)) + { + track.Start(); + } + } + + /// + /// Stops playback + /// + /// The ID of the track to stop playback on + public void Stop(int trackId) + { + if (m_TrackPool.TryGet(trackId, out SoundIoAudioTrack track)) + { + track.Stop(); + } + } + + /// + /// Appends an audio buffer to the specified track + /// + /// The sample type of the buffer + /// The track to append the buffer to + /// The internal tag of the buffer + /// The buffer to append to the track + public void AppendBuffer(int trackId, long bufferTag, T[] buffer) + where T : struct + { + if(m_TrackPool.TryGet(trackId, out SoundIoAudioTrack track)) + { + track.AppendBuffer(bufferTag, buffer); + } + } + + /// + /// Returns a value indicating whether the specified buffer is currently reserved by the specified track + /// + /// The track to check + /// The buffer tag to check + public bool ContainsBuffer(int trackId, long bufferTag) + { + if (m_TrackPool.TryGet(trackId, out SoundIoAudioTrack track)) + { + return track.ContainsBuffer(bufferTag); + } + + return false; + } + + /// + /// Gets a list of buffer tags the specified track is no longer reserving + /// + /// The track to retrieve buffer tags from + /// The maximum amount of buffer tags to retrieve + /// Buffers released by the specified track + public long[] GetReleasedBuffers(int trackId, int maxCount) + { + if (m_TrackPool.TryGet(trackId, out SoundIoAudioTrack track)) + { + List bufferTags = new List(); + + while(maxCount-- > 0 && track.ReleasedBuffers.TryDequeue(out long tag)) + { + bufferTags.Add(tag); + } + + return bufferTags.ToArray(); + } + + return new long[0]; + } + + /// + /// Releases the unmanaged resources used by the + /// + public void Dispose() + { + m_TrackPool.Dispose(); + m_AudioContext.Disconnect(); + m_AudioContext.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs new file mode 100644 index 0000000000..97ba11d513 --- /dev/null +++ b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrack.cs @@ -0,0 +1,560 @@ +using SoundIOSharp; +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Audio.SoundIo +{ + internal class SoundIoAudioTrack : IDisposable + { + /// + /// The audio track ring buffer + /// + private SoundIoRingBuffer m_Buffer; + + /// + /// A list of buffers currently pending writeback to the audio backend + /// + private ConcurrentQueue m_ReservedBuffers; + + /// + /// Occurs when a buffer has been released by the audio backend + /// + private event ReleaseCallback BufferReleased; + + /// + /// The track ID of this + /// + public int TrackID { get; private set; } + + /// + /// The current playback state + /// + public PlaybackState State { get; private set; } + + /// + /// The audio context this track belongs to + /// + public SoundIO AudioContext { get; private set; } + + /// + /// The this track belongs to + /// + public SoundIODevice AudioDevice { get; private set; } + + /// + /// The audio output stream of this track + /// + public SoundIOOutStream AudioStream { get; private set; } + + /// + /// Released buffers the track is no longer holding + /// + public ConcurrentQueue ReleasedBuffers { get; private set; } + + /// + /// Constructs a new instance of a + /// + /// The track ID + /// The SoundIO audio context + /// The SoundIO audio device + public SoundIoAudioTrack(int trackId, SoundIO audioContext, SoundIODevice audioDevice) + { + TrackID = trackId; + AudioContext = audioContext; + AudioDevice = audioDevice; + State = PlaybackState.Stopped; + ReleasedBuffers = new ConcurrentQueue(); + + m_Buffer = new SoundIoRingBuffer(); + m_ReservedBuffers = new ConcurrentQueue(); + } + + /// + /// Opens the audio track with the specified parameters + /// + /// The requested sample rate of the track + /// The requested channel count of the track + /// A that represents the delegate to invoke when a buffer has been released by the audio track + /// The requested sample format of the track + public void Open( + int sampleRate, + int channelCount, + ReleaseCallback callback, + SoundIOFormat format = SoundIOFormat.S16LE) + { + // Close any existing audio streams + if (AudioStream != null) + { + Close(); + } + + if (!AudioDevice.SupportsSampleRate(sampleRate)) + { + throw new InvalidOperationException($"This sound device does not support a sample rate of {sampleRate}Hz"); + } + + if (!AudioDevice.SupportsFormat(format)) + { + throw new InvalidOperationException($"This sound device does not support SoundIOFormat.{Enum.GetName(typeof(SoundIOFormat), format)}"); + } + + AudioStream = AudioDevice.CreateOutStream(); + + AudioStream.Name = $"SwitchAudioTrack_{TrackID}"; + AudioStream.Layout = SoundIOChannelLayout.GetDefault(channelCount); + AudioStream.Format = format; + AudioStream.SampleRate = sampleRate; + + AudioStream.WriteCallback = WriteCallback; + + BufferReleased += callback; + + AudioStream.Open(); + } + + /// + /// This callback occurs when the sound device is ready to buffer more frames + /// + /// The minimum amount of frames expected by the audio backend + /// The maximum amount of frames that can be written to the audio backend + private unsafe void WriteCallback(int minFrameCount, int maxFrameCount) + { + int bytesPerFrame = AudioStream.BytesPerFrame; + uint bytesPerSample = (uint)AudioStream.BytesPerSample; + + int bufferedFrames = m_Buffer.Length / bytesPerFrame; + long bufferedSamples = m_Buffer.Length / bytesPerSample; + + int frameCount = Math.Min(bufferedFrames, maxFrameCount); + + if (frameCount == 0) + { + return; + } + + SoundIOChannelAreas areas = AudioStream.BeginWrite(ref frameCount); + int channelCount = areas.ChannelCount; + + byte[] samples = new byte[frameCount * bytesPerFrame]; + + m_Buffer.Read(samples, 0, samples.Length); + + // This is a huge ugly block of code, but we save + // a significant amount of time over the generic + // loop that handles other channel counts. + + // Mono + if (channelCount == 1) + { + SoundIOChannelArea area = areas.GetArea(0); + + fixed (byte* srcptr = samples) + { + if (bytesPerSample == 1) + { + for (int frame = 0; frame < frameCount; frame++) + { + ((byte*)area.Pointer)[0] = srcptr[frame * bytesPerFrame]; + + area.Pointer += area.Step; + } + } + else if (bytesPerSample == 2) + { + for (int frame = 0; frame < frameCount; frame++) + { + ((short*)area.Pointer)[0] = ((short*)srcptr)[frame * bytesPerFrame >> 1]; + + area.Pointer += area.Step; + } + } + else if (bytesPerSample == 4) + { + for (int frame = 0; frame < frameCount; frame++) + { + ((int*)area.Pointer)[0] = ((int*)srcptr)[frame * bytesPerFrame >> 2]; + + area.Pointer += area.Step; + } + } + else + { + for (int frame = 0; frame < frameCount; frame++) + { + Unsafe.CopyBlockUnaligned((byte*)area.Pointer, srcptr + (frame * bytesPerFrame), bytesPerSample); + + area.Pointer += area.Step; + } + } + } + } + // Stereo + else if (channelCount == 2) + { + SoundIOChannelArea area1 = areas.GetArea(0); + SoundIOChannelArea area2 = areas.GetArea(1); + + fixed (byte* srcptr = samples) + { + if (bytesPerSample == 1) + { + for (int frame = 0; frame < frameCount; frame++) + { + // Channel 1 + ((byte*)area1.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 0]; + + // Channel 2 + ((byte*)area2.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 1]; + + area1.Pointer += area1.Step; + area2.Pointer += area2.Step; + } + } + else if (bytesPerSample == 2) + { + for (int frame = 0; frame < frameCount; frame++) + { + // Channel 1 + ((short*)area1.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 0]; + + // Channel 2 + ((short*)area2.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 1]; + + area1.Pointer += area1.Step; + area2.Pointer += area2.Step; + } + } + else if (bytesPerSample == 4) + { + for (int frame = 0; frame < frameCount; frame++) + { + // Channel 1 + ((int*)area1.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 0]; + + // Channel 2 + ((int*)area2.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 1]; + + area1.Pointer += area1.Step; + area2.Pointer += area2.Step; + } + } + else + { + for (int frame = 0; frame < frameCount; frame++) + { + // Channel 1 + Unsafe.CopyBlockUnaligned((byte*)area1.Pointer, srcptr + (frame * bytesPerFrame) + (0 * bytesPerSample), bytesPerSample); + + // Channel 2 + Unsafe.CopyBlockUnaligned((byte*)area2.Pointer, srcptr + (frame * bytesPerFrame) + (1 * bytesPerSample), bytesPerSample); + + area1.Pointer += area1.Step; + area2.Pointer += area2.Step; + } + } + } + } + // Surround + else if (channelCount == 6) + { + SoundIOChannelArea area1 = areas.GetArea(0); + SoundIOChannelArea area2 = areas.GetArea(1); + SoundIOChannelArea area3 = areas.GetArea(2); + SoundIOChannelArea area4 = areas.GetArea(3); + SoundIOChannelArea area5 = areas.GetArea(4); + SoundIOChannelArea area6 = areas.GetArea(5); + + fixed (byte* srcptr = samples) + { + if (bytesPerSample == 1) + { + for (int frame = 0; frame < frameCount; frame++) + { + // Channel 1 + ((byte*)area1.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 0]; + + // Channel 2 + ((byte*)area2.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 1]; + + // Channel 3 + ((byte*)area3.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 2]; + + // Channel 4 + ((byte*)area4.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 3]; + + // Channel 5 + ((byte*)area5.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 4]; + + // Channel 6 + ((byte*)area6.Pointer)[0] = srcptr[(frame * bytesPerFrame) + 5]; + + area1.Pointer += area1.Step; + area2.Pointer += area2.Step; + area3.Pointer += area3.Step; + area4.Pointer += area4.Step; + area5.Pointer += area5.Step; + area6.Pointer += area6.Step; + } + } + else if (bytesPerSample == 2) + { + for (int frame = 0; frame < frameCount; frame++) + { + // Channel 1 + ((short*)area1.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 0]; + + // Channel 2 + ((short*)area2.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 1]; + + // Channel 3 + ((short*)area3.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 2]; + + // Channel 4 + ((short*)area4.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 3]; + + // Channel 5 + ((short*)area5.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 4]; + + // Channel 6 + ((short*)area6.Pointer)[0] = ((short*)srcptr)[(frame * bytesPerFrame >> 1) + 5]; + + area1.Pointer += area1.Step; + area2.Pointer += area2.Step; + area3.Pointer += area3.Step; + area4.Pointer += area4.Step; + area5.Pointer += area5.Step; + area6.Pointer += area6.Step; + } + } + else if (bytesPerSample == 4) + { + for (int frame = 0; frame < frameCount; frame++) + { + // Channel 1 + ((int*)area1.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 0]; + + // Channel 2 + ((int*)area2.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 1]; + + // Channel 3 + ((int*)area3.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 2]; + + // Channel 4 + ((int*)area4.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 3]; + + // Channel 5 + ((int*)area5.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 4]; + + // Channel 6 + ((int*)area6.Pointer)[0] = ((int*)srcptr)[(frame * bytesPerFrame >> 2) + 5]; + + area1.Pointer += area1.Step; + area2.Pointer += area2.Step; + area3.Pointer += area3.Step; + area4.Pointer += area4.Step; + area5.Pointer += area5.Step; + area6.Pointer += area6.Step; + } + } + else + { + for (int frame = 0; frame < frameCount; frame++) + { + // Channel 1 + Unsafe.CopyBlockUnaligned((byte*)area1.Pointer, srcptr + (frame * bytesPerFrame) + (0 * bytesPerSample), bytesPerSample); + + // Channel 2 + Unsafe.CopyBlockUnaligned((byte*)area2.Pointer, srcptr + (frame * bytesPerFrame) + (1 * bytesPerSample), bytesPerSample); + + // Channel 3 + Unsafe.CopyBlockUnaligned((byte*)area3.Pointer, srcptr + (frame * bytesPerFrame) + (2 * bytesPerSample), bytesPerSample); + + // Channel 4 + Unsafe.CopyBlockUnaligned((byte*)area4.Pointer, srcptr + (frame * bytesPerFrame) + (3 * bytesPerSample), bytesPerSample); + + // Channel 5 + Unsafe.CopyBlockUnaligned((byte*)area5.Pointer, srcptr + (frame * bytesPerFrame) + (4 * bytesPerSample), bytesPerSample); + + // Channel 6 + Unsafe.CopyBlockUnaligned((byte*)area6.Pointer, srcptr + (frame * bytesPerFrame) + (5 * bytesPerSample), bytesPerSample); + + area1.Pointer += area1.Step; + area2.Pointer += area2.Step; + area3.Pointer += area3.Step; + area4.Pointer += area4.Step; + area5.Pointer += area5.Step; + area6.Pointer += area6.Step; + } + } + } + } + // Every other channel count + else + { + SoundIOChannelArea[] channels = new SoundIOChannelArea[channelCount]; + + // Obtain the channel area for each channel + for (int i = 0; i < channelCount; i++) + { + channels[i] = areas.GetArea(i); + } + + fixed (byte* srcptr = samples) + { + for (int frame = 0; frame < frameCount; frame++) + for (int channel = 0; channel < areas.ChannelCount; channel++) + { + // Copy channel by channel, frame by frame. This is slow! + Unsafe.CopyBlockUnaligned((byte*)channels[channel].Pointer, srcptr + (frame * bytesPerFrame) + (channel * bytesPerSample), bytesPerSample); + + channels[channel].Pointer += channels[channel].Step; + } + } + } + + AudioStream.EndWrite(); + + UpdateReleasedBuffers(samples.Length); + } + + /// + /// Releases any buffers that have been fully written to the output device + /// + /// The amount of bytes written in the last device write + private void UpdateReleasedBuffers(int bytesRead) + { + bool bufferReleased = false; + + while (bytesRead > 0) + { + if (m_ReservedBuffers.TryPeek(out SoundIoBuffer buffer)) + { + if (buffer.Length > bytesRead) + { + buffer.Length -= bytesRead; + bytesRead = 0; + } + else + { + bufferReleased = true; + bytesRead -= buffer.Length; + + m_ReservedBuffers.TryDequeue(out buffer); + ReleasedBuffers.Enqueue(buffer.Tag); + } + } + } + + if (bufferReleased) + { + OnBufferReleased(); + } + } + + /// + /// Starts audio playback + /// + public void Start() + { + if (AudioStream == null) + { + return; + } + + AudioStream.Start(); + AudioStream.Pause(false); + AudioContext.FlushEvents(); + State = PlaybackState.Playing; + } + + /// + /// Stops audio playback + /// + public void Stop() + { + if (AudioStream == null) + { + return; + } + + AudioStream.Pause(true); + AudioContext.FlushEvents(); + State = PlaybackState.Stopped; + } + + /// + /// Appends an audio buffer to the tracks internal ring buffer + /// + /// The audio sample type + /// The unqiue tag of the buffer being appended + /// The buffer to append + public void AppendBuffer(long bufferTag, T[] buffer) + { + if (AudioStream == null) + { + return; + } + + // Calculate the size of the audio samples + int size = Unsafe.SizeOf(); + + // Calculate the amount of bytes to copy from the buffer + int bytesToCopy = size * buffer.Length; + + // Copy the memory to our ring buffer + m_Buffer.Write(buffer, 0, bytesToCopy); + + // Keep track of "buffered" buffers + m_ReservedBuffers.Enqueue(new SoundIoBuffer(bufferTag, bytesToCopy)); + } + + /// + /// Returns a value indicating whether the specified buffer is currently reserved by the track + /// + /// The buffer tag to check + public bool ContainsBuffer(long bufferTag) + { + return m_ReservedBuffers.Any(x => x.Tag == bufferTag); + } + + /// + /// Closes the + /// + public void Close() + { + if (AudioStream != null) + { + AudioStream.Pause(true); + AudioStream.Dispose(); + } + + m_Buffer.Clear(); + OnBufferReleased(); + ReleasedBuffers.Clear(); + + State = PlaybackState.Stopped; + AudioStream = null; + BufferReleased = null; + } + + private void OnBufferReleased() + { + BufferReleased?.Invoke(); + } + + /// + /// Releases the unmanaged resources used by the + /// + public void Dispose() + { + Close(); + } + + ~SoundIoAudioTrack() + { + Dispose(); + } + } +} diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs new file mode 100644 index 0000000000..ec256e2086 --- /dev/null +++ b/Ryujinx.Audio/Renderers/SoundIo/SoundIoAudioTrackPool.cs @@ -0,0 +1,193 @@ +using SoundIOSharp; +using System; +using System.Collections.Concurrent; +using System.Linq; + +namespace Ryujinx.Audio.SoundIo +{ + /// + /// An object pool containing a set of audio tracks + /// + internal class SoundIoAudioTrackPool : IDisposable + { + /// + /// The current size of the + /// + private int m_Size; + + /// + /// The maximum size of the + /// + private int m_MaxSize; + + /// + /// The audio context this track pool belongs to + /// + private SoundIO m_Context; + + /// + /// The audio device this track pool belongs to + /// + private SoundIODevice m_Device; + + /// + /// The queue that keeps track of the available in the pool. + /// + private ConcurrentQueue m_Queue; + + /// + /// The dictionary providing mapping between a TrackID and + /// + private ConcurrentDictionary m_TrackList; + + /// + /// Gets the current size of the + /// + public int Size { get => m_Size; } + + /// + /// Gets the maximum size of the + /// + public int MaxSize { get => m_MaxSize; } + + /// + /// Gets a value that indicates whether the is empty + /// + public bool IsEmpty { get => m_Queue.IsEmpty; } + + /// + /// Constructs a new instance of a that is empty + /// + /// The maximum amount of tracks that can be created + public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize) + { + m_Size = 0; + m_Context = context; + m_Device = device; + m_MaxSize = maxSize; + + m_Queue = new ConcurrentQueue(); + m_TrackList = new ConcurrentDictionary(); + } + + /// + /// Constructs a new instance of a that contains + /// the specified amount of + /// + /// The maximum amount of tracks that can be created + /// The initial number of tracks that the pool contains + public SoundIoAudioTrackPool(SoundIO context, SoundIODevice device, int maxSize, int initialCapacity) + : this(context, device, maxSize) + { + var trackCollection = Enumerable.Range(0, initialCapacity) + .Select(TrackFactory); + + m_Size = initialCapacity; + m_Queue = new ConcurrentQueue(trackCollection); + } + + /// + /// Creates a new with the proper AudioContext and AudioDevice + /// and the specified + /// + /// The ID of the track to be created + /// A new AudioTrack with the specified ID + private SoundIoAudioTrack TrackFactory(int trackId) + { + // Create a new AudioTrack + SoundIoAudioTrack track = new SoundIoAudioTrack(trackId, m_Context, m_Device); + + // Keep track of issued tracks + m_TrackList[trackId] = track; + + return track; + } + + /// + /// Retrieves a from the pool + /// + /// An AudioTrack from the pool + public SoundIoAudioTrack Get() + { + // If we have a track available, reuse it + if (m_Queue.TryDequeue(out SoundIoAudioTrack track)) + { + return track; + } + + // Have we reached the maximum size of our pool? + if (m_Size >= m_MaxSize) + { + return null; + } + + // We don't have any pooled tracks, so create a new one + return TrackFactory(m_Size++); + } + + /// + /// Retrieves the associated with the specified from the pool + /// + /// The ID of the track to retrieve + public SoundIoAudioTrack Get(int trackId) + { + if (m_TrackList.TryGetValue(trackId, out SoundIoAudioTrack track)) + { + return track; + } + + return null; + } + + /// + /// Attempers to get a from the pool + /// + /// The track retrieved from the pool + /// True if retrieve was successful + public bool TryGet(out SoundIoAudioTrack track) + { + track = Get(); + + return track != null; + } + + /// + /// Attempts to get the associated with the specified from the pool + /// + /// The ID of the track to retrieve + /// The track retrieved from the pool + public bool TryGet(int trackId, out SoundIoAudioTrack track) + { + return m_TrackList.TryGetValue(trackId, out track); + } + + /// + /// Returns an back to the pool for reuse + /// + /// The track to be returned to the pool + public void Put(SoundIoAudioTrack track) + { + // Ensure the track is disposed and not playing audio + track.Close(); + + // Requeue the track for reuse later + m_Queue.Enqueue(track); + } + + /// + /// Releases the unmanaged resources used by the + /// + public void Dispose() + { + foreach (var track in m_TrackList) + { + track.Value.Close(); + track.Value.Dispose(); + } + + m_Size = 0; + m_Queue.Clear(); + m_TrackList.Clear(); + } + } +} diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs new file mode 100644 index 0000000000..2a6190b53b --- /dev/null +++ b/Ryujinx.Audio/Renderers/SoundIo/SoundIoBuffer.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Audio.SoundIo +{ + /// + /// Represents the remaining bytes left buffered for a specific buffer tag + /// + internal class SoundIoBuffer + { + /// + /// The buffer tag this represents + /// + public long Tag { get; private set; } + + /// + /// The remaining bytes still to be released + /// + public int Length { get; set; } + + /// + /// Constructs a new instance of a + /// + /// The buffer tag + /// The size of the buffer + public SoundIoBuffer(long tag, int length) + { + Tag = tag; + Length = length; + } + } +} diff --git a/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs b/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs new file mode 100644 index 0000000000..b288502132 --- /dev/null +++ b/Ryujinx.Audio/Renderers/SoundIo/SoundIoRingBuffer.cs @@ -0,0 +1,204 @@ +using System; + +namespace Ryujinx.Audio.SoundIo +{ + /// + /// A thread-safe variable-size circular buffer + /// + internal class SoundIoRingBuffer + { + private byte[] m_Buffer; + private int m_Size; + private int m_HeadOffset; + private int m_TailOffset; + + /// + /// Gets the available bytes in the ring buffer + /// + public int Length + { + get { return m_Size; } + } + + /// + /// Constructs a new instance of a + /// + public SoundIoRingBuffer() + { + m_Buffer = new byte[2048]; + } + + /// + /// Constructs a new instance of a with the specified capacity + /// + /// The number of entries that the can initially contain + public SoundIoRingBuffer(int capacity) + { + m_Buffer = new byte[capacity]; + } + + /// + /// Clears the ring buffer + /// + public void Clear() + { + m_Size = 0; + m_HeadOffset = 0; + m_TailOffset = 0; + } + + /// + /// Clears the specified amount of bytes from the ring buffer + /// + /// The amount of bytes to clear from the ring buffer + public void Clear(int size) + { + lock (this) + { + if (size > m_Size) + { + size = m_Size; + } + + if (size == 0) + { + return; + } + + m_HeadOffset = (m_HeadOffset + size) % m_Buffer.Length; + m_Size -= size; + + if (m_Size == 0) + { + m_HeadOffset = 0; + m_TailOffset = 0; + } + + return; + } + } + + /// + /// Extends the capacity of the ring buffer + /// + private void SetCapacity(int capacity) + { + byte[] buffer = new byte[capacity]; + + if (m_Size > 0) + { + if (m_HeadOffset < m_TailOffset) + { + Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, 0, m_Size); + } + else + { + Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, 0, m_Buffer.Length - m_HeadOffset); + Buffer.BlockCopy(m_Buffer, 0, buffer, m_Buffer.Length - m_HeadOffset, m_TailOffset); + } + } + + m_Buffer = buffer; + m_HeadOffset = 0; + m_TailOffset = m_Size; + } + + + /// + /// Writes a sequence of bytes to the ring buffer + /// + /// A byte array containing the data to write + /// The zero-based byte offset in from which to begin copying bytes to the ring buffer + /// The number of bytes to write + public void Write(T[] buffer, int index, int count) + { + if (count == 0) + { + return; + } + + lock (this) + { + if ((m_Size + count) > m_Buffer.Length) + { + SetCapacity((m_Size + count + 2047) & ~2047); + } + + if (m_HeadOffset < m_TailOffset) + { + int tailLength = m_Buffer.Length - m_TailOffset; + + if (tailLength >= count) + { + Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, count); + } + else + { + Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, tailLength); + Buffer.BlockCopy(buffer, index + tailLength, m_Buffer, 0, count - tailLength); + } + } + else + { + Buffer.BlockCopy(buffer, index, m_Buffer, m_TailOffset, count); + } + + m_Size += count; + m_TailOffset = (m_TailOffset + count) % m_Buffer.Length; + } + } + + /// + /// Reads a sequence of bytes from the ring buffer and advances the position within the ring buffer by the number of bytes read + /// + /// The buffer to write the data into + /// The zero-based byte offset in at which the read bytes will be placed + /// The maximum number of bytes to read + /// The total number of bytes read into the buffer. This might be less than the number of bytes requested if that number of bytes are not currently available, or zero if the ring buffer is empty + public int Read(T[] buffer, int index, int count) + { + lock (this) + { + if (count > m_Size) + { + count = m_Size; + } + + if (count == 0) + { + return 0; + } + + if (m_HeadOffset < m_TailOffset) + { + Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, count); + } + else + { + int tailLength = m_Buffer.Length - m_HeadOffset; + + if (tailLength >= count) + { + Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, count); + } + else + { + Buffer.BlockCopy(m_Buffer, m_HeadOffset, buffer, index, tailLength); + Buffer.BlockCopy(m_Buffer, 0, buffer, index + tailLength, count - tailLength); + } + } + + m_Size -= count; + m_HeadOffset = (m_HeadOffset + count) % m_Buffer.Length; + + if (m_Size == 0) + { + m_HeadOffset = 0; + m_TailOffset = 0; + } + + return count; + } + } + } +} diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj index 2cd38add9b..82d2a4d152 100644 --- a/Ryujinx.Audio/Ryujinx.Audio.csproj +++ b/Ryujinx.Audio/Ryujinx.Audio.csproj @@ -5,8 +5,36 @@ win10-x64;osx-x64;linux-x64 + + true + + + + true + + + + + + + + + + + + PreserveNewest + libsoundio.dll + + + PreserveNewest + libsoundio.dylib + + + PreserveNewest + libsoundio.so + diff --git a/Ryujinx.HLE/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs similarity index 88% rename from Ryujinx.HLE/Logging/LogClass.cs rename to Ryujinx.Common/Logging/LogClass.cs index 95cae7e0ab..8739fbc677 100644 --- a/Ryujinx.HLE/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.HLE.Logging +namespace Ryujinx.Common.Logging { public enum LogClass { @@ -22,6 +22,8 @@ namespace Ryujinx.HLE.Logging ServiceFriend, ServiceFs, ServiceHid, + ServiceIrs, + ServiceLdr, ServiceLm, ServiceMm, ServiceNfp, @@ -31,6 +33,7 @@ namespace Ryujinx.HLE.Logging ServicePctl, ServicePl, ServicePrepo, + ServicePsm, ServiceSet, ServiceSfdnsres, ServiceSm, diff --git a/Ryujinx.HLE/Logging/LogEventArgs.cs b/Ryujinx.Common/Logging/LogEventArgs.cs similarity index 92% rename from Ryujinx.HLE/Logging/LogEventArgs.cs rename to Ryujinx.Common/Logging/LogEventArgs.cs index 647cf71319..7a479b71c7 100644 --- a/Ryujinx.HLE/Logging/LogEventArgs.cs +++ b/Ryujinx.Common/Logging/LogEventArgs.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.HLE.Logging +namespace Ryujinx.Common.Logging { public class LogEventArgs : EventArgs { diff --git a/Ryujinx.HLE/Logging/LogLevel.cs b/Ryujinx.Common/Logging/LogLevel.cs similarity index 77% rename from Ryujinx.HLE/Logging/LogLevel.cs rename to Ryujinx.Common/Logging/LogLevel.cs index 971333e60a..ba3fa99f4d 100644 --- a/Ryujinx.HLE/Logging/LogLevel.cs +++ b/Ryujinx.Common/Logging/LogLevel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.HLE.Logging +namespace Ryujinx.Common.Logging { public enum LogLevel { diff --git a/Ryujinx.HLE/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs similarity index 57% rename from Ryujinx.HLE/Logging/Logger.cs rename to Ryujinx.Common/Logging/Logger.cs index 5376b253a3..6422f11313 100644 --- a/Ryujinx.HLE/Logging/Logger.cs +++ b/Ryujinx.Common/Logging/Logger.cs @@ -2,18 +2,18 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; -namespace Ryujinx.HLE.Logging +namespace Ryujinx.Common.Logging { - public class Logger + public static class Logger { - private bool[] EnabledLevels; - private bool[] EnabledClasses; + private static bool[] EnabledLevels; + private static bool[] EnabledClasses; - public event EventHandler Updated; + public static event EventHandler Updated; - private Stopwatch Time; + private static Stopwatch Time; - public Logger() + static Logger() { EnabledLevels = new bool[Enum.GetNames(typeof(LogLevel)).Length]; EnabledClasses = new bool[Enum.GetNames(typeof(LogClass)).Length]; @@ -33,50 +33,50 @@ namespace Ryujinx.HLE.Logging Time.Start(); } - public void SetEnable(LogLevel Level, bool Enabled) + public static void SetEnable(LogLevel Level, bool Enabled) { EnabledLevels[(int)Level] = Enabled; } - public void SetEnable(LogClass Class, bool Enabled) + public static void SetEnable(LogClass Class, bool Enabled) { EnabledClasses[(int)Class] = Enabled; } - internal void PrintDebug(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintDebug(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Debug, Class, GetFormattedMessage(Class, Message, Caller)); } - internal void PrintStub(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintStub(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Stub, Class, GetFormattedMessage(Class, Message, Caller)); } - internal void PrintInfo(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintInfo(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Info, Class, GetFormattedMessage(Class, Message, Caller)); } - internal void PrintWarning(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintWarning(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Warning, Class, GetFormattedMessage(Class, Message, Caller)); } - internal void PrintError(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintError(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Error, Class, GetFormattedMessage(Class, Message, Caller)); } - private void Print(LogLevel Level, LogClass Class, string Message) + private static void Print(LogLevel Level, LogClass Class, string Message) { if (EnabledLevels[(int)Level] && EnabledClasses[(int)Class]) { - Updated?.Invoke(this, new LogEventArgs(Level, Time.Elapsed, Message)); + Updated?.Invoke(null, new LogEventArgs(Level, Time.Elapsed, Message)); } } - private string GetFormattedMessage(LogClass Class, string Message, string Caller) + private static string GetFormattedMessage(LogClass Class, string Message, string Caller) { return $"{Class} {Caller}: {Message}"; } diff --git a/Ryujinx.Common/PerformanceCounter.cs b/Ryujinx.Common/PerformanceCounter.cs new file mode 100644 index 0000000000..4c8ae6a75a --- /dev/null +++ b/Ryujinx.Common/PerformanceCounter.cs @@ -0,0 +1,65 @@ +using System.Diagnostics; + +namespace Ryujinx.Common +{ + public static class PerformanceCounter + { + /// + /// Represents the number of ticks in 1 day. + /// + public static long TicksPerDay { get; } + + /// + /// Represents the number of ticks in 1 hour. + /// + public static long TicksPerHour { get; } + + /// + /// Represents the number of ticks in 1 minute. + /// + public static long TicksPerMinute { get; } + + /// + /// Represents the number of ticks in 1 second. + /// + public static long TicksPerSecond { get; } + + /// + /// Represents the number of ticks in 1 millisecond. + /// + public static long TicksPerMillisecond { get; } + + /// + /// Gets the number of milliseconds elapsed since the system started. + /// + public static long ElapsedTicks + { + get + { + return Stopwatch.GetTimestamp(); + } + } + + /// + /// Gets the number of milliseconds elapsed since the system started. + /// + public static long ElapsedMilliseconds + { + get + { + long timestamp = Stopwatch.GetTimestamp(); + + return timestamp / TicksPerMillisecond; + } + } + + static PerformanceCounter() + { + TicksPerMillisecond = Stopwatch.Frequency / 1000; + TicksPerSecond = Stopwatch.Frequency; + TicksPerMinute = TicksPerSecond * 60; + TicksPerHour = TicksPerMinute * 60; + TicksPerDay = TicksPerHour * 24; + } + } +} diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj new file mode 100644 index 0000000000..5c9293b707 --- /dev/null +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp2.1 + win10-x64;osx-x64;linux-x64 + + + + true + + + + true + + + diff --git a/Ryujinx.Graphics/DmaPusher.cs b/Ryujinx.Graphics/DmaPusher.cs new file mode 100644 index 0000000000..608d8a1d1a --- /dev/null +++ b/Ryujinx.Graphics/DmaPusher.cs @@ -0,0 +1,190 @@ +using Ryujinx.Graphics.Memory; +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.Graphics +{ + public class DmaPusher + { + private ConcurrentQueue<(NvGpuVmm, long)> IbBuffer; + + private long DmaPut; + private long DmaGet; + + private struct DmaState + { + public int Method; + public int SubChannel; + public int MethodCount; + public bool NonIncrementing; + public bool IncrementOnce; + public int LengthPending; + } + + private DmaState State; + + private bool SliEnable; + private bool SliActive; + + private bool IbEnable; + private bool NonMain; + + private long DmaMGet; + + private NvGpuVmm Vmm; + + private NvGpu Gpu; + + private AutoResetEvent Event; + + public DmaPusher(NvGpu Gpu) + { + this.Gpu = Gpu; + + IbBuffer = new ConcurrentQueue<(NvGpuVmm, long)>(); + + IbEnable = true; + + Event = new AutoResetEvent(false); + } + + public void Push(NvGpuVmm Vmm, long Entry) + { + IbBuffer.Enqueue((Vmm, Entry)); + + Event.Set(); + } + + public bool WaitForCommands() + { + return Event.WaitOne(8); + } + + public void DispatchCalls() + { + while (Step()); + } + + private bool Step() + { + if (DmaGet != DmaPut) + { + int Word = Vmm.ReadInt32(DmaGet); + + DmaGet += 4; + + if (!NonMain) + { + DmaMGet = DmaGet; + } + + if (State.LengthPending != 0) + { + State.LengthPending = 0; + State.MethodCount = Word & 0xffffff; + } + else if (State.MethodCount != 0) + { + if (!SliEnable || SliActive) + { + CallMethod(Word); + } + + if (!State.NonIncrementing) + { + State.Method++; + } + + if (State.IncrementOnce) + { + State.NonIncrementing = true; + } + + State.MethodCount--; + } + else + { + int SumissionMode = (Word >> 29) & 7; + + switch (SumissionMode) + { + case 1: + //Incrementing. + SetNonImmediateState(Word); + + State.NonIncrementing = false; + State.IncrementOnce = false; + + break; + + case 3: + //Non-incrementing. + SetNonImmediateState(Word); + + State.NonIncrementing = true; + State.IncrementOnce = false; + + break; + + case 4: + //Immediate. + State.Method = (Word >> 0) & 0x1fff; + State.SubChannel = (Word >> 13) & 7; + State.NonIncrementing = true; + State.IncrementOnce = false; + + CallMethod((Word >> 16) & 0x1fff); + + break; + + case 5: + //Increment-once. + SetNonImmediateState(Word); + + State.NonIncrementing = false; + State.IncrementOnce = true; + + break; + } + } + } + else if (IbEnable && IbBuffer.TryDequeue(out (NvGpuVmm Vmm, long Entry) Tuple)) + { + this.Vmm = Tuple.Vmm; + + long Entry = Tuple.Entry; + + int Length = (int)(Entry >> 42) & 0x1fffff; + + DmaGet = Entry & 0xfffffffffc; + DmaPut = DmaGet + Length * 4; + + NonMain = (Entry & (1L << 41)) != 0; + + Gpu.ResourceManager.ClearPbCache(); + } + else + { + return false; + } + + return true; + } + + private void SetNonImmediateState(int Word) + { + State.Method = (Word >> 0) & 0x1fff; + State.SubChannel = (Word >> 13) & 7; + State.MethodCount = (Word >> 16) & 0x1fff; + } + + private void CallMethod(int Argument) + { + Gpu.Fifo.CallMethod(Vmm, new GpuMethodCall( + State.Method, + Argument, + State.SubChannel, + State.MethodCount)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalBlendEquation.cs b/Ryujinx.Graphics/Gal/GalBlendEquation.cs index 7fd4ba5fa6..7757faae90 100644 --- a/Ryujinx.Graphics/Gal/GalBlendEquation.cs +++ b/Ryujinx.Graphics/Gal/GalBlendEquation.cs @@ -6,6 +6,12 @@ namespace Ryujinx.Graphics.Gal FuncSubtract = 2, FuncReverseSubtract = 3, Min = 4, - Max = 5 + Max = 5, + + FuncAddGl = 0x8006, + FuncSubtractGl = 0x8007, + FuncReverseSubtractGl = 0x8008, + MinGl = 0x800a, + MaxGl = 0x800b } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalBlendFactor.cs b/Ryujinx.Graphics/Gal/GalBlendFactor.cs index 001aaaeca8..f70b050118 100644 --- a/Ryujinx.Graphics/Gal/GalBlendFactor.cs +++ b/Ryujinx.Graphics/Gal/GalBlendFactor.cs @@ -21,6 +21,25 @@ namespace Ryujinx.Graphics.Gal OneMinusConstantColor = 0x62, ConstantAlpha = 0x63, OneMinusConstantAlpha = 0x64, - ConstantColorG80 = 0xc001 + + ZeroGl = 0x4000, + OneGl = 0x4001, + SrcColorGl = 0x4300, + OneMinusSrcColorGl = 0x4301, + SrcAlphaGl = 0x4302, + OneMinusSrcAlphaGl = 0x4303, + DstAlphaGl = 0x4304, + OneMinusDstAlphaGl = 0x4305, + DstColorGl = 0x4306, + OneMinusDstColorGl = 0x4307, + SrcAlphaSaturateGl = 0x4308, + ConstantColorGl = 0xc001, + OneMinusConstantColorGl = 0xc002, + ConstantAlphaGl = 0xc003, + OneMinusConstantAlphaGl = 0xc004, + Src1ColorGl = 0xc900, + OneMinusSrc1ColorGl = 0xc901, + Src1AlphaGl = 0xc902, + OneMinusSrc1AlphaGl = 0xc903 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalImage.cs b/Ryujinx.Graphics/Gal/GalImage.cs index dc6f02e044..92f43cc9d4 100644 --- a/Ryujinx.Graphics/Gal/GalImage.cs +++ b/Ryujinx.Graphics/Gal/GalImage.cs @@ -1,12 +1,17 @@ +using Ryujinx.Graphics.Texture; + namespace Ryujinx.Graphics.Gal { public struct GalImage { public int Width; public int Height; + public int TileWidth; + public int GobBlockHeight; + public int Pitch; - public GalImageFormat Format; - + public GalImageFormat Format; + public GalMemoryLayout Layout; public GalTextureSource XSource; public GalTextureSource YSource; public GalTextureSource ZSource; @@ -15,19 +20,44 @@ namespace Ryujinx.Graphics.Gal public GalImage( int Width, int Height, + int TileWidth, + int GobBlockHeight, + GalMemoryLayout Layout, GalImageFormat Format, GalTextureSource XSource = GalTextureSource.Red, GalTextureSource YSource = GalTextureSource.Green, GalTextureSource ZSource = GalTextureSource.Blue, GalTextureSource WSource = GalTextureSource.Alpha) { - this.Width = Width; - this.Height = Height; - this.Format = Format; - this.XSource = XSource; - this.YSource = YSource; - this.ZSource = ZSource; - this.WSource = WSource; + this.Width = Width; + this.Height = Height; + this.TileWidth = TileWidth; + this.GobBlockHeight = GobBlockHeight; + this.Layout = Layout; + this.Format = Format; + this.XSource = XSource; + this.YSource = YSource; + this.ZSource = ZSource; + this.WSource = WSource; + + Pitch = ImageUtils.GetPitch(Format, Width); + } + + public bool SizeMatches(GalImage Image) + { + if (ImageUtils.GetBytesPerPixel(Format) != + ImageUtils.GetBytesPerPixel(Image.Format)) + { + return false; + } + + if (ImageUtils.GetAlignedWidth(this) != + ImageUtils.GetAlignedWidth(Image)) + { + return false; + } + + return Height == Image.Height; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalImageFormat.cs b/Ryujinx.Graphics/Gal/GalImageFormat.cs index ba555684f2..83c7f5697b 100644 --- a/Ryujinx.Graphics/Gal/GalImageFormat.cs +++ b/Ryujinx.Graphics/Gal/GalImageFormat.cs @@ -5,89 +5,63 @@ namespace Ryujinx.Graphics.Gal [Flags] public enum GalImageFormat { - Snorm = 1 << 27, - Unorm = 1 << 28, - Sint = 1 << 29, - Uint = 1 << 30, - Sfloat = 1 << 31, + Astc2DStart, + Astc2D4x4, + Astc2D5x4, + Astc2D5x5, + Astc2D6x5, + Astc2D6x6, + Astc2D8x5, + Astc2D8x6, + Astc2D8x8, + Astc2D10x5, + Astc2D10x6, + Astc2D10x8, + Astc2D10x10, + Astc2D12x10, + Astc2D12x12, + Astc2DEnd, - TypeMask = Snorm | Unorm | Sint | Uint | Sfloat, - - FormatMask = ~TypeMask, - - ASTC_BEGIN = ASTC_4x4, - - ASTC_4x4 = 1, - ASTC_5x4, - ASTC_5x5, - ASTC_6x5, - ASTC_6x6, - ASTC_8x5, - ASTC_8x6, - ASTC_8x8, - ASTC_10x5, - ASTC_10x6, - ASTC_10x8, - ASTC_10x10, - ASTC_12x10, - ASTC_12x12, - - ASTC_END = ASTC_12x12, - - R4G4, - R4G4B4A4, - B4G4R4A4, - A4B4G4R4, - R5G6B5, - B5G6R5, - R5G5B5A1, - B5G5R5A1, - A1R5G5B5, + RGBA4, + RGB565, + BGR5A1, + RGB5A1, R8, - R8G8, - G8R8, - R8G8B8, - B8G8R8, - R8G8B8A8, - B8G8R8A8, - A8B8G8R8, - A8B8G8R8_SRGB, - A2R10G10B10, - A2B10G10R10, + RG8, + RGBX8, + RGBA8, + BGRA8, + RGB10A2, R16, - R16G16, - R16G16B16, - R16G16B16A16, + RG16, + RGBA16, R32, - R32G32, - R32G32B32, - R32G32B32A32, - R64, - R64G64, - R64G64B64, - R64G64B64A64, - B10G11R11, - E5B9G9R9, + RG32, + RGBA32, + R11G11B10, D16, - X8_D24, + D24, D32, - S8, - D16_S8, - D24_S8, - D32_S8, - BC1_RGB, - BC1_RGBA, + D24S8, + D32S8, + BC1, BC2, BC3, BC4, BC5, - BC6H_SF16, - BC6H_UF16, - BC7, - ETC2_R8G8B8, - ETC2_R8G8B8A1, - ETC2_R8G8B8A8, - EAC_R11, - EAC_R11G11, + BptcSfloat, + BptcUfloat, + BptcUnorm, + + Snorm = 1 << 26, + Unorm = 1 << 27, + Sint = 1 << 28, + Uint = 1 << 39, + Float = 1 << 30, + Srgb = 1 << 31, + + TypeMask = Snorm | Unorm | Sint | Uint | Float | Srgb, + + FormatMask = ~TypeMask } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalMemoryLayout.cs b/Ryujinx.Graphics/Gal/GalMemoryLayout.cs new file mode 100644 index 0000000000..73fabf8c2a --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalMemoryLayout.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalMemoryLayout + { + BlockLinear = 0, + Pitch = 1 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs index 7c66951415..8630da9cc4 100644 --- a/Ryujinx.Graphics/Gal/GalPipelineState.cs +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -1,26 +1,61 @@ namespace Ryujinx.Graphics.Gal { - public struct GalVertexBinding + public struct ColorMaskState { - //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3 + private static readonly ColorMaskState _Default = new ColorMaskState() + { + Red = true, + Green = true, + Blue = true, + Alpha = true + }; - public bool Enabled; - public int Stride; - public long VboKey; - public bool Instanced; - public int Divisor; - public GalVertexAttrib[] Attribs; + public static ColorMaskState Default => _Default; + + public bool Red; + public bool Green; + public bool Blue; + public bool Alpha; + } + + public struct BlendState + { + private static readonly BlendState _Default = new BlendState() + { + Enabled = false, + SeparateAlpha = false, + EquationRgb = GalBlendEquation.FuncAdd, + FuncSrcRgb = GalBlendFactor.One, + FuncDstRgb = GalBlendFactor.Zero, + EquationAlpha = GalBlendEquation.FuncAdd, + FuncSrcAlpha = GalBlendFactor.One, + FuncDstAlpha = GalBlendFactor.Zero + }; + + public static BlendState Default => _Default; + + public bool Enabled; + public bool SeparateAlpha; + public GalBlendEquation EquationRgb; + public GalBlendFactor FuncSrcRgb; + public GalBlendFactor FuncDstRgb; + public GalBlendEquation EquationAlpha; + public GalBlendFactor FuncSrcAlpha; + public GalBlendFactor FuncDstAlpha; } public class GalPipelineState { - public const int Stages = 5; + public const int Stages = 5; public const int ConstBuffersPerStage = 18; + public const int RenderTargetsCount = 8; public long[][] ConstBufferKeys; public GalVertexBinding[] VertexBindings; + public bool FramebufferSrgb; + public float FlipX; public float FlipY; @@ -28,38 +63,39 @@ public GalFrontFace FrontFace; - public bool CullFaceEnabled; + public bool CullFaceEnabled; public GalCullFace CullFace; - public bool DepthTestEnabled; + public bool DepthTestEnabled; + public bool DepthWriteEnabled; public GalComparisonOp DepthFunc; + public float DepthRangeNear; + public float DepthRangeFar; public bool StencilTestEnabled; + public bool StencilTwoSideEnabled; public GalComparisonOp StencilBackFuncFunc; - public int StencilBackFuncRef; - public uint StencilBackFuncMask; - public GalStencilOp StencilBackOpFail; - public GalStencilOp StencilBackOpZFail; - public GalStencilOp StencilBackOpZPass; - public uint StencilBackMask; + public int StencilBackFuncRef; + public uint StencilBackFuncMask; + public GalStencilOp StencilBackOpFail; + public GalStencilOp StencilBackOpZFail; + public GalStencilOp StencilBackOpZPass; + public uint StencilBackMask; public GalComparisonOp StencilFrontFuncFunc; - public int StencilFrontFuncRef; - public uint StencilFrontFuncMask; - public GalStencilOp StencilFrontOpFail; - public GalStencilOp StencilFrontOpZFail; - public GalStencilOp StencilFrontOpZPass; - public uint StencilFrontMask; + public int StencilFrontFuncRef; + public uint StencilFrontFuncMask; + public GalStencilOp StencilFrontOpFail; + public GalStencilOp StencilFrontOpZFail; + public GalStencilOp StencilFrontOpZPass; + public uint StencilFrontMask; - public bool BlendEnabled; - public bool BlendSeparateAlpha; - public GalBlendEquation BlendEquationRgb; - public GalBlendFactor BlendFuncSrcRgb; - public GalBlendFactor BlendFuncDstRgb; - public GalBlendEquation BlendEquationAlpha; - public GalBlendFactor BlendFuncSrcAlpha; - public GalBlendFactor BlendFuncDstAlpha; + public bool BlendIndependent; + public BlendState[] Blends; + + public bool ColorMaskCommon; + public ColorMaskState[] ColorMasks; public bool PrimitiveRestartEnabled; public uint PrimitiveRestartIndex; @@ -72,6 +108,10 @@ { ConstBufferKeys[Stage] = new long[ConstBuffersPerStage]; } + + Blends = new BlendState[RenderTargetsCount]; + + ColorMasks = new ColorMaskState[RenderTargetsCount]; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs b/Ryujinx.Graphics/Gal/GalSurfaceFormat.cs similarity index 100% rename from Ryujinx.Graphics/Gal/GalFrameBufferFormat.cs rename to Ryujinx.Graphics/Gal/GalSurfaceFormat.cs diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index 009d2b826e..51ce577970 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -2,43 +2,45 @@ namespace Ryujinx.Graphics.Gal { public enum GalTextureFormat { - R32G32B32A32 = 0x1, - R16G16B16A16 = 0x3, - R32G32 = 0x4, - A8B8G8R8 = 0x8, - A2B10G10R10 = 0x9, - R32 = 0xf, - BC6H_SF16 = 0x10, - BC6H_UF16 = 0x11, - A4B4G4R4 = 0x12, - A1B5G5R5 = 0x14, - B5G6R5 = 0x15, - BC7U = 0x17, - G8R8 = 0x18, - R16 = 0x1b, - R8 = 0x1d, - BF10GF11RF11 = 0x21, - BC1 = 0x24, - BC2 = 0x25, - BC3 = 0x26, - BC4 = 0x27, - BC5 = 0x28, - Z24S8 = 0x29, - ZF32 = 0x2f, - ZF32_X24S8 = 0x30, - Astc2D4x4 = 0x40, - Astc2D5x5 = 0x41, - Astc2D6x6 = 0x42, - Astc2D8x8 = 0x44, - Astc2D10x10 = 0x45, - Astc2D12x12 = 0x46, - Astc2D5x4 = 0x50, - Astc2D6x5 = 0x51, - Astc2D8x6 = 0x52, - Astc2D10x8 = 0x53, - Astc2D12x10 = 0x54, - Astc2D8x5 = 0x55, - Astc2D10x5 = 0x56, - Astc2D10x6 = 0x57 + RGBA32 = 0x1, + RGBA16 = 0x3, + RG32 = 0x4, + RGBA8 = 0x8, + RGB10A2 = 0x9, + RG16 = 0xc, + R32 = 0xf, + BptcSfloat = 0x10, + BptcUfloat = 0x11, + RGBA4 = 0x12, + RGB5A1 = 0x14, + RGB565 = 0x15, + BptcUnorm = 0x17, + RG8 = 0x18, + R16 = 0x1b, + R8 = 0x1d, + R11G11B10F = 0x21, + BC1 = 0x24, + BC2 = 0x25, + BC3 = 0x26, + BC4 = 0x27, + BC5 = 0x28, + D24S8 = 0x29, + D32F = 0x2f, + D32FX24S8 = 0x30, + D16 = 0x3a, + Astc2D4x4 = 0x40, + Astc2D5x5 = 0x41, + Astc2D6x6 = 0x42, + Astc2D8x8 = 0x44, + Astc2D10x10 = 0x45, + Astc2D12x12 = 0x46, + Astc2D5x4 = 0x50, + Astc2D6x5 = 0x51, + Astc2D8x6 = 0x52, + Astc2D10x8 = 0x53, + Astc2D12x10 = 0x54, + Astc2D8x5 = 0x55, + Astc2D10x5 = 0x56, + Astc2D10x6 = 0x57 } } diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs index dd04006025..fa9a391f71 100644 --- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs +++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs @@ -1,10 +1,13 @@ +using System; + namespace Ryujinx.Graphics.Gal { public struct GalVertexAttrib { - public int Index { get; private set; } - public bool IsConst { get; private set; } - public int Offset { get; private set; } + public int Index { get; private set; } + public bool IsConst { get; private set; } + public int Offset { get; private set; } + public IntPtr Pointer { get; private set; } public GalVertexAttribSize Size { get; private set; } public GalVertexAttribType Type { get; private set; } @@ -15,12 +18,14 @@ namespace Ryujinx.Graphics.Gal int Index, bool IsConst, int Offset, + IntPtr Pointer, GalVertexAttribSize Size, GalVertexAttribType Type, bool IsBgra) { this.Index = Index; this.IsConst = IsConst; + this.Pointer = Pointer; this.Offset = Offset; this.Size = Size; this.Type = Type; diff --git a/Ryujinx.Graphics/Gal/GalVertexBinding.cs b/Ryujinx.Graphics/Gal/GalVertexBinding.cs new file mode 100644 index 0000000000..0c3c845d4f --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalVertexBinding.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal +{ + public struct GalVertexBinding + { + //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3 + + public bool Enabled; + public int Stride; + public long VboKey; + public bool Instanced; + public int Divisor; + public GalVertexAttrib[] Attribs; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalZetaFormat.cs b/Ryujinx.Graphics/Gal/GalZetaFormat.cs index 759e312170..2429249e54 100644 --- a/Ryujinx.Graphics/Gal/GalZetaFormat.cs +++ b/Ryujinx.Graphics/Gal/GalZetaFormat.cs @@ -2,15 +2,15 @@ { public enum GalZetaFormat { - Z32Float = 0x0a, - Z16Unorm = 0x13, - S8Z24Unorm = 0x14, - Z24X8Unorm = 0x15, - Z24S8Unorm = 0x16, - Z24C8Unorm = 0x18, - Z32S8X24Float = 0x19, - Z24X8S8C8X16Unorm = 0x1d, - Z32X8C8X16Float = 0x1e, - Z32S8C8X16Float = 0x1f + D32Float = 0x0a, + D16Unorm = 0x13, + S8D24Unorm = 0x14, + D24X8Unorm = 0x15, + D24S8Unorm = 0x16, + D24C8Unorm = 0x18, + D32S8X24Float = 0x19, + D24X8S8C8X16Unorm = 0x1d, + D32X8C8X16Float = 0x1e, + D32S8C8X16Float = 0x1f } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalPipeline.cs b/Ryujinx.Graphics/Gal/IGalPipeline.cs index d8cf266af4..cba4e7dcc6 100644 --- a/Ryujinx.Graphics/Gal/IGalPipeline.cs +++ b/Ryujinx.Graphics/Gal/IGalPipeline.cs @@ -3,5 +3,8 @@ public interface IGalPipeline { void Bind(GalPipelineState State); + + void ResetDepthMask(); + void ResetColorMask(int Index); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index a20b6f5322..052e3f35f0 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -10,7 +10,10 @@ namespace Ryujinx.Graphics.Gal void ClearBuffers( GalClearBufferFlags Flags, int Attachment, - float Red, float Green, float Blue, float Alpha, + float Red, + float Green, + float Blue, + float Alpha, float Depth, int Stencil); @@ -21,10 +24,11 @@ namespace Ryujinx.Graphics.Gal void CreateVbo(long Key, int DataSize, IntPtr HostAddress); void CreateIbo(long Key, int DataSize, IntPtr HostAddress); + void CreateIbo(long Key, int DataSize, byte[] Buffer); void SetIndexArray(int Size, GalIndexFormat Format); - void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType); + void DrawArrays(int First, int Count, GalPrimitiveType PrimType); void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType); } diff --git a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs index c44434ef69..f941ccd584 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs @@ -1,9 +1,9 @@ -using System; - namespace Ryujinx.Graphics.Gal { public interface IGalRenderTarget { + void Bind(); + void BindColor(long Key, int Attachment); void UnbindColor(int Attachment); @@ -12,11 +12,7 @@ namespace Ryujinx.Graphics.Gal void UnbindZeta(); - void BindTexture(long Key, int Index); - - void Set(long Key); - - void Set(byte[] Data, int Width, int Height); + void Present(long Key); void SetMap(int[] Map); @@ -24,7 +20,7 @@ namespace Ryujinx.Graphics.Gal void SetWindowSize(int Width, int Height); - void SetViewport(int X, int Y, int Width, int Height); + void SetViewport(int Attachment, int X, int Y, int Width, int Height); void Render(); @@ -40,12 +36,6 @@ namespace Ryujinx.Graphics.Gal int DstX1, int DstY1); - void GetBufferData(long Key, Action Callback); - - void SetBufferData( - long Key, - int Width, - int Height, - byte[] Buffer); + void Reinterpret(long Key, GalImage NewImage); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalTexture.cs b/Ryujinx.Graphics/Gal/IGalTexture.cs index 292f59efa1..aeecdf1ac5 100644 --- a/Ryujinx.Graphics/Gal/IGalTexture.cs +++ b/Ryujinx.Graphics/Gal/IGalTexture.cs @@ -5,13 +5,13 @@ namespace Ryujinx.Graphics.Gal void LockCache(); void UnlockCache(); + void Create(long Key, int Size, GalImage Image); + void Create(long Key, byte[] Data, GalImage Image); - void CreateFb(long Key, long Size, GalImage Image); + bool TryGetImage(long Key, out GalImage Image); - bool TryGetCachedTexture(long Key, long DataSize, out GalImage Image); - - void Bind(long Key, int Index); + void Bind(long Key, int Index, GalImage Image); void SetSampler(GalTextureSampler Sampler); } diff --git a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs index dda825385e..8db0b8a8c9 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs @@ -1,14 +1,9 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.Texture; -using System; +using Ryujinx.Graphics.Texture; namespace Ryujinx.Graphics.Gal.OpenGL { class ImageHandler { - private static int CopyBuffer = 0; - private static int CopyBufferSize = 0; - public GalImage Image { get; private set; } public int Width => Image.Width; @@ -16,144 +11,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL public GalImageFormat Format => Image.Format; - public PixelInternalFormat InternalFormat { get; private set; } - public PixelFormat PixelFormat { get; private set; } - public PixelType PixelType { get; private set; } - public int Handle { get; private set; } - private bool Initialized; - - public ImageHandler() - { - Handle = GL.GenTexture(); - } - - public ImageHandler(int Handle, GalImage Image) - { - this.Handle = Handle; - - this.Image = Image; - } - - public void EnsureSetup(GalImage NewImage) - { - if (Width == NewImage.Width && - Height == NewImage.Height && - Format == NewImage.Format && - Initialized) - { - return; - } - - PixelInternalFormat InternalFmt; - PixelFormat PixelFormat; - PixelType PixelType; - - if (ImageUtils.IsCompressed(NewImage.Format)) - { - InternalFmt = (PixelInternalFormat)OGLEnumConverter.GetCompressedImageFormat(NewImage.Format); - - PixelFormat = default(PixelFormat); - PixelType = default(PixelType); - } - else - { - (InternalFmt, PixelFormat, PixelType) = OGLEnumConverter.GetImageFormat(NewImage.Format); - } - - GL.BindTexture(TextureTarget.Texture2D, Handle); - - if (Initialized) - { - if (CopyBuffer == 0) - { - CopyBuffer = GL.GenBuffer(); - } - - int CurrentSize = Math.Max(ImageUtils.GetSize(NewImage), - ImageUtils.GetSize(Image)); - - GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer); - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer); - - if (CopyBufferSize < CurrentSize) - { - CopyBufferSize = CurrentSize; - - GL.BufferData(BufferTarget.PixelPackBuffer, CurrentSize, IntPtr.Zero, BufferUsageHint.StreamCopy); - } - - if (ImageUtils.IsCompressed(Image.Format)) - { - GL.GetCompressedTexImage(TextureTarget.Texture2D, 0, IntPtr.Zero); - } - else - { - GL.GetTexImage(TextureTarget.Texture2D, 0, this.PixelFormat, this.PixelType, IntPtr.Zero); - } - - GL.DeleteTexture(Handle); - - Handle = GL.GenTexture(); - - GL.BindTexture(TextureTarget.Texture2D, Handle); - } - - const int MinFilter = (int)TextureMinFilter.Linear; - const int MagFilter = (int)TextureMagFilter.Linear; - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter); - - const int Level = 0; - const int Border = 0; - - if (ImageUtils.IsCompressed(NewImage.Format)) - { - Console.WriteLine("Hit"); - - GL.CompressedTexImage2D( - TextureTarget.Texture2D, - Level, - (InternalFormat)InternalFmt, - NewImage.Width, - NewImage.Height, - Border, - ImageUtils.GetSize(NewImage), - IntPtr.Zero); - } - else - { - GL.TexImage2D( - TextureTarget.Texture2D, - Level, - InternalFmt, - NewImage.Width, - NewImage.Height, - Border, - PixelFormat, - PixelType, - IntPtr.Zero); - } - - if (Initialized) - { - GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); - } - - Image = NewImage; - - this.InternalFormat = InternalFmt; - this.PixelFormat = PixelFormat; - this.PixelType = PixelType; - - Initialized = true; - } - public bool HasColor => ImageUtils.HasColor(Image.Format); public bool HasDepth => ImageUtils.HasDepth(Image.Format); public bool HasStencil => ImageUtils.HasStencil(Image.Format); + + public ImageHandler(int Handle, GalImage Image) + { + this.Handle = Handle; + this.Image = Image; + } } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs index 01ebf98202..6e17872ba0 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using System; using System.Collections.Generic; @@ -7,7 +8,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public delegate void DeleteValue(T Value); - private const int MaxTimeDelta = 5 * 60000; + private const int MinTimeDelta = 5 * 60000; private const int MaxRemovalsPerRun = 10; private struct CacheBucket @@ -18,15 +19,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL public long DataSize { get; private set; } - public int Timestamp { get; private set; } + public long Timestamp { get; private set; } public CacheBucket(T Value, long DataSize, LinkedListNode Node) { - this.Value = Value; - this.DataSize = DataSize; - this.Node = Node; + this.Value = Value; + this.DataSize = DataSize; + this.Node = Node; - Timestamp = Environment.TickCount; + Timestamp = PerformanceCounter.ElapsedMilliseconds; } } @@ -40,8 +41,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL private bool Locked; - public OGLCachedResource(DeleteValue DeleteValueCallback) + private long MaxSize; + private long TotalSize; + + public OGLCachedResource(DeleteValue DeleteValueCallback, long MaxSize) { + this.MaxSize = MaxSize; + if (DeleteValueCallback == null) { throw new ArgumentNullException(nameof(DeleteValueCallback)); @@ -97,12 +103,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL SortedCache.Remove(Bucket.Node); + TotalSize -= Bucket.DataSize; + Cache[Key] = NewBucket; } else { Cache.Add(Key, NewBucket); } + + TotalSize += Size; } public bool TryGetValue(long Key, out T Value) @@ -141,7 +151,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL private void ClearCacheIfNeeded() { - int Timestamp = Environment.TickCount; + long Timestamp = PerformanceCounter.ElapsedMilliseconds; int Count = 0; @@ -156,9 +166,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL CacheBucket Bucket = Cache[Node.Value]; - int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp); + long TimeDelta = Timestamp - Bucket.Timestamp; - if ((uint)TimeDelta <= (uint)MaxTimeDelta) + if (TimeDelta <= MinTimeDelta && !UnderMemoryPressure()) { break; } @@ -168,19 +178,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL Cache.Remove(Node.Value); DeleteValueCallback(Bucket.Value); + + TotalSize -= Bucket.DataSize; } } - private int RingDelta(int Old, int New) + private bool UnderMemoryPressure() { - if ((uint)New < (uint)Old) - { - return New + (~Old + 1); - } - else - { - return New - Old; - } + return TotalSize >= MaxSize; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs index 4958b53b3b..e04190e042 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs @@ -5,11 +5,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL { class OGLConstBuffer : IGalConstBuffer { + private const long MaxConstBufferCacheSize = 64 * 1024 * 1024; + private OGLCachedResource Cache; public OGLConstBuffer() { - Cache = new OGLCachedResource(DeleteBuffer); + Cache = new OGLCachedResource(DeleteBuffer, MaxConstBufferCacheSize); } public void LockCache() diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 1d4f4cf777..918163be49 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalFrontFace.CCW: return FrontFaceDirection.Ccw; } - throw new ArgumentException(nameof(FrontFace)); + throw new ArgumentException(nameof(FrontFace) + " \"" + FrontFace + "\" is not valid!"); } public static CullFaceMode GetCullFace(GalCullFace CullFace) @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack; } - throw new ArgumentException(nameof(CullFace)); + throw new ArgumentException(nameof(CullFace) + " \"" + CullFace + "\" is not valid!"); } public static StencilOp GetStencilOp(GalStencilOp Op) @@ -42,37 +42,40 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalStencilOp.DecrWrap: return StencilOp.DecrWrap; } - throw new ArgumentException(nameof(Op)); + throw new ArgumentException(nameof(Op) + " \"" + Op + "\" is not valid!"); } public static DepthFunction GetDepthFunc(GalComparisonOp Func) { - //Looks like the GPU can take it's own values (described in GalComparisonOp) and OpenGL values alike - if ((int)Func >= (int)DepthFunction.Never && - (int)Func <= (int)DepthFunction.Always) - { - return (DepthFunction)Func; - } - - switch (Func) - { - case GalComparisonOp.Never: return DepthFunction.Never; - case GalComparisonOp.Less: return DepthFunction.Less; - case GalComparisonOp.Equal: return DepthFunction.Equal; - case GalComparisonOp.Lequal: return DepthFunction.Lequal; - case GalComparisonOp.Greater: return DepthFunction.Greater; - case GalComparisonOp.NotEqual: return DepthFunction.Notequal; - case GalComparisonOp.Gequal: return DepthFunction.Gequal; - case GalComparisonOp.Always: return DepthFunction.Always; - } - - throw new ArgumentException(nameof(Func)); + return (DepthFunction)GetFunc(Func); } public static StencilFunction GetStencilFunc(GalComparisonOp Func) { - //OGL comparison values match, it's just an enum cast - return (StencilFunction)GetDepthFunc(Func); + return (StencilFunction)GetFunc(Func); + } + + private static All GetFunc(GalComparisonOp Func) + { + if ((int)Func >= (int)All.Never && + (int)Func <= (int)All.Always) + { + return (All)Func; + } + + switch (Func) + { + case GalComparisonOp.Never: return All.Never; + case GalComparisonOp.Less: return All.Less; + case GalComparisonOp.Equal: return All.Equal; + case GalComparisonOp.Lequal: return All.Lequal; + case GalComparisonOp.Greater: return All.Greater; + case GalComparisonOp.NotEqual: return All.Notequal; + case GalComparisonOp.Gequal: return All.Gequal; + case GalComparisonOp.Always: return All.Always; + } + + throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!"); } public static DrawElementsType GetDrawElementsType(GalIndexFormat Format) @@ -84,7 +87,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt; } - throw new ArgumentException(nameof(Format)); + throw new ArgumentException(nameof(Format) + " \"" + Format + "\" is not valid!"); } public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type) @@ -98,8 +101,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalPrimitiveType.Triangles: return PrimitiveType.Triangles; case GalPrimitiveType.TriangleStrip: return PrimitiveType.TriangleStrip; case GalPrimitiveType.TriangleFan: return PrimitiveType.TriangleFan; - case GalPrimitiveType.Quads: return PrimitiveType.Quads; - case GalPrimitiveType.QuadStrip: return PrimitiveType.QuadStrip; case GalPrimitiveType.Polygon: return PrimitiveType.Polygon; case GalPrimitiveType.LinesAdjacency: return PrimitiveType.LinesAdjacency; case GalPrimitiveType.LineStripAdjacency: return PrimitiveType.LineStripAdjacency; @@ -108,7 +109,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalPrimitiveType.Patches: return PrimitiveType.Patches; } - throw new ArgumentException(nameof(Type)); + throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!"); } public static ShaderType GetShaderType(GalShaderType Type) @@ -122,80 +123,90 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalShaderType.Fragment: return ShaderType.FragmentShader; } - throw new ArgumentException(nameof(Type)); + throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!"); } public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat Format) { switch (Format) { - case GalImageFormat.R32G32B32A32 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float); - case GalImageFormat.R32G32B32A32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int); - case GalImageFormat.R32G32B32A32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt); - case GalImageFormat.R16G16B16A16 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat); - case GalImageFormat.R16G16B16A16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short); - case GalImageFormat.R16G16B16A16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort); - case GalImageFormat.R32G32 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float); - case GalImageFormat.R32G32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int); - case GalImageFormat.R32G32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt); - case GalImageFormat.A8B8G8R8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte); - case GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte); - case GalImageFormat.A8B8G8R8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte); - case GalImageFormat.A8B8G8R8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte); - case GalImageFormat.A8B8G8R8_SRGB: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte); - case GalImageFormat.A4B4G4R4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed); - case GalImageFormat.A2B10G10R10 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed); - case GalImageFormat.A2B10G10R10 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); - case GalImageFormat.R32 | GalImageFormat.Sfloat: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float); - case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int); - case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt); - case GalImageFormat.A1R5G5B5 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551); - case GalImageFormat.B5G6R5 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565); - case GalImageFormat.R16G16 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat); - case GalImageFormat.R16G16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short); - case GalImageFormat.R16G16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Byte); - case GalImageFormat.R16G16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort); - case GalImageFormat.R8G8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte); - case GalImageFormat.R8G8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte); - case GalImageFormat.R8G8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte); - case GalImageFormat.R8G8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte); - case GalImageFormat.R16 | GalImageFormat.Sfloat: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat); - case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short); - case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Byte); - case GalImageFormat.R16 | GalImageFormat.Uint: return (PixelInternalFormat.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort); - case GalImageFormat.R16 | GalImageFormat.Unorm: return (PixelInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort); - case GalImageFormat.R8 | GalImageFormat.Sint: return (PixelInternalFormat.R8i, PixelFormat.RedInteger, PixelType.Byte); - case GalImageFormat.R8 | GalImageFormat.Snorm: return (PixelInternalFormat.R8Snorm, PixelFormat.Red, PixelType.Byte); - case GalImageFormat.R8 | GalImageFormat.Uint: return (PixelInternalFormat.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte); - case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte); - case GalImageFormat.B10G11R11 | GalImageFormat.Sfloat: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); + case GalImageFormat.RGBA32 | GalImageFormat.Float: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float); + case GalImageFormat.RGBA32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int); + case GalImageFormat.RGBA32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt); + case GalImageFormat.RGBA16 | GalImageFormat.Float: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat); + case GalImageFormat.RGBA16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short); + case GalImageFormat.RGBA16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort); + case GalImageFormat.RG32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float); + case GalImageFormat.RG32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int); + case GalImageFormat.RG32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt); + case GalImageFormat.RGBX8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte); + case GalImageFormat.RGBA8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte); + case GalImageFormat.RGBA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte); + case GalImageFormat.RGBA8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte); + case GalImageFormat.RGBA8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte); + case GalImageFormat.RGBA8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte); + case GalImageFormat.BGRA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte); + case GalImageFormat.RGBA4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed); + case GalImageFormat.RGB10A2 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed); + case GalImageFormat.RGB10A2 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); + case GalImageFormat.R32 | GalImageFormat.Float: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float); + case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int); + case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt); + case GalImageFormat.BGR5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551); + case GalImageFormat.RGB5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed); + case GalImageFormat.RGB565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed); + case GalImageFormat.RG16 | GalImageFormat.Float: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat); + case GalImageFormat.RG16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short); + case GalImageFormat.RG16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Short); + case GalImageFormat.RG16 | GalImageFormat.Uint: return (PixelInternalFormat.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort); + case GalImageFormat.RG16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort); + case GalImageFormat.RG8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte); + case GalImageFormat.RG8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte); + case GalImageFormat.RG8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte); + case GalImageFormat.RG8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte); + case GalImageFormat.R16 | GalImageFormat.Float: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat); + case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short); + case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Short); + case GalImageFormat.R16 | GalImageFormat.Uint: return (PixelInternalFormat.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort); + case GalImageFormat.R16 | GalImageFormat.Unorm: return (PixelInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort); + case GalImageFormat.R8 | GalImageFormat.Sint: return (PixelInternalFormat.R8i, PixelFormat.RedInteger, PixelType.Byte); + case GalImageFormat.R8 | GalImageFormat.Snorm: return (PixelInternalFormat.R8Snorm, PixelFormat.Red, PixelType.Byte); + case GalImageFormat.R8 | GalImageFormat.Uint: return (PixelInternalFormat.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte); + case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte); + case GalImageFormat.R11G11B10 | GalImageFormat.Float: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); - case GalImageFormat.D24_S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); - case GalImageFormat.D32 | GalImageFormat.Sfloat: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float); - case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort); - case GalImageFormat.D32_S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev); + case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort); + case GalImageFormat.D24 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt); + case GalImageFormat.D24S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); + case GalImageFormat.D24S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); + case GalImageFormat.D32 | GalImageFormat.Float: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float); + case GalImageFormat.D32S8 | GalImageFormat.Float: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev); } - throw new NotImplementedException(Format.ToString()); + throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}"); } public static InternalFormat GetCompressedImageFormat(GalImageFormat Format) { switch (Format) { - case GalImageFormat.BC6H_UF16 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbBptcUnsignedFloat; - case GalImageFormat.BC6H_SF16 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbBptcSignedFloat; - case GalImageFormat.BC7 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaBptcUnorm; - case GalImageFormat.BC1_RGBA | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt1Ext; - case GalImageFormat.BC2 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt3Ext; - case GalImageFormat.BC3 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt5Ext; - case GalImageFormat.BC4 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRedRgtc1; - case GalImageFormat.BC4 | GalImageFormat.Unorm: return InternalFormat.CompressedRedRgtc1; - case GalImageFormat.BC5 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRgRgtc2; - case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2; + case GalImageFormat.BptcSfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcSignedFloat; + case GalImageFormat.BptcUfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcUnsignedFloat; + case GalImageFormat.BptcUnorm | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaBptcUnorm; + case GalImageFormat.BptcUnorm | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaBptcUnorm; + case GalImageFormat.BC1 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt1Ext; + case GalImageFormat.BC1 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt1Ext; + case GalImageFormat.BC2 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt3Ext; + case GalImageFormat.BC2 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt3Ext; + case GalImageFormat.BC3 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt5Ext; + case GalImageFormat.BC3 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt5Ext; + case GalImageFormat.BC4 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRedRgtc1; + case GalImageFormat.BC4 | GalImageFormat.Unorm: return InternalFormat.CompressedRedRgtc1; + case GalImageFormat.BC5 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRgRgtc2; + case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2; } - throw new NotImplementedException(Format.ToString()); + throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}"); } public static All GetTextureSwizzle(GalTextureSource Source) @@ -211,7 +222,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureSource.OneFloat: return All.One; } - throw new ArgumentException(nameof(Source)); + throw new ArgumentException(nameof(Source) + " \"" + Source + "\" is not valid!"); } public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap) @@ -225,7 +236,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureWrap.Clamp: return TextureWrapMode.Clamp; } - if (OGLExtension.HasTextureMirrorClamp()) + if (OGLExtension.TextureMirrorClamp) { switch (Wrap) { @@ -245,7 +256,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - throw new ArgumentException(nameof(Wrap)); + throw new ArgumentException(nameof(Wrap) + " \"" + Wrap + "\" is not valid!"); } public static TextureMinFilter GetTextureMinFilter( @@ -259,7 +270,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureFilter.Linear: return TextureMinFilter.Linear; } - throw new ArgumentException(nameof(MinFilter)); + throw new ArgumentException(nameof(MinFilter) + " \"" + MinFilter + "\" is not valid!"); } public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter) @@ -270,52 +281,119 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureFilter.Linear: return TextureMagFilter.Linear; } - throw new ArgumentException(nameof(Filter)); + throw new ArgumentException(nameof(Filter) + " \"" + Filter + "\" is not valid!"); } public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation) { switch (BlendEquation) { - case GalBlendEquation.FuncAdd: return BlendEquationMode.FuncAdd; - case GalBlendEquation.FuncSubtract: return BlendEquationMode.FuncSubtract; - case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract; - case GalBlendEquation.Min: return BlendEquationMode.Min; - case GalBlendEquation.Max: return BlendEquationMode.Max; + case GalBlendEquation.FuncAdd: + case GalBlendEquation.FuncAddGl: + return BlendEquationMode.FuncAdd; + + case GalBlendEquation.FuncSubtract: + case GalBlendEquation.FuncSubtractGl: + return BlendEquationMode.FuncSubtract; + + case GalBlendEquation.FuncReverseSubtract: + case GalBlendEquation.FuncReverseSubtractGl: + return BlendEquationMode.FuncReverseSubtract; + + case GalBlendEquation.Min: + case GalBlendEquation.MinGl: + return BlendEquationMode.Min; + + case GalBlendEquation.Max: + case GalBlendEquation.MaxGl: + return BlendEquationMode.Max; } - throw new ArgumentException(nameof(BlendEquation)); + throw new ArgumentException(nameof(BlendEquation) + " \"" + BlendEquation + "\" is not valid!"); } public static BlendingFactor GetBlendFactor(GalBlendFactor BlendFactor) { switch (BlendFactor) { - case GalBlendFactor.Zero: return BlendingFactor.Zero; - case GalBlendFactor.One: return BlendingFactor.One; - case GalBlendFactor.SrcColor: return BlendingFactor.SrcColor; - case GalBlendFactor.OneMinusSrcColor: return BlendingFactor.OneMinusSrcColor; - case GalBlendFactor.DstColor: return BlendingFactor.DstColor; - case GalBlendFactor.OneMinusDstColor: return BlendingFactor.OneMinusDstColor; - case GalBlendFactor.SrcAlpha: return BlendingFactor.SrcAlpha; - case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactor.OneMinusSrcAlpha; - case GalBlendFactor.DstAlpha: return BlendingFactor.DstAlpha; - case GalBlendFactor.OneMinusDstAlpha: return BlendingFactor.OneMinusDstAlpha; - case GalBlendFactor.OneMinusConstantColor: return BlendingFactor.OneMinusConstantColor; - case GalBlendFactor.ConstantAlpha: return BlendingFactor.ConstantAlpha; - case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactor.OneMinusConstantAlpha; - case GalBlendFactor.SrcAlphaSaturate: return BlendingFactor.SrcAlphaSaturate; - case GalBlendFactor.Src1Color: return BlendingFactor.Src1Color; - case GalBlendFactor.OneMinusSrc1Color: return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Color; - case GalBlendFactor.Src1Alpha: return BlendingFactor.Src1Alpha; - case GalBlendFactor.OneMinusSrc1Alpha: return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Alpha; + case GalBlendFactor.Zero: + case GalBlendFactor.ZeroGl: + return BlendingFactor.Zero; + + case GalBlendFactor.One: + case GalBlendFactor.OneGl: + return BlendingFactor.One; + + case GalBlendFactor.SrcColor: + case GalBlendFactor.SrcColorGl: + return BlendingFactor.SrcColor; + + case GalBlendFactor.OneMinusSrcColor: + case GalBlendFactor.OneMinusSrcColorGl: + return BlendingFactor.OneMinusSrcColor; + + case GalBlendFactor.DstColor: + case GalBlendFactor.DstColorGl: + return BlendingFactor.DstColor; + + case GalBlendFactor.OneMinusDstColor: + case GalBlendFactor.OneMinusDstColorGl: + return BlendingFactor.OneMinusDstColor; + + case GalBlendFactor.SrcAlpha: + case GalBlendFactor.SrcAlphaGl: + return BlendingFactor.SrcAlpha; + + case GalBlendFactor.OneMinusSrcAlpha: + case GalBlendFactor.OneMinusSrcAlphaGl: + return BlendingFactor.OneMinusSrcAlpha; + + case GalBlendFactor.DstAlpha: + case GalBlendFactor.DstAlphaGl: + return BlendingFactor.DstAlpha; + + case GalBlendFactor.OneMinusDstAlpha: + case GalBlendFactor.OneMinusDstAlphaGl: + return BlendingFactor.OneMinusDstAlpha; + + case GalBlendFactor.OneMinusConstantColor: + case GalBlendFactor.OneMinusConstantColorGl: + return BlendingFactor.OneMinusConstantColor; + + case GalBlendFactor.ConstantAlpha: + case GalBlendFactor.ConstantAlphaGl: + return BlendingFactor.ConstantAlpha; + + case GalBlendFactor.OneMinusConstantAlpha: + case GalBlendFactor.OneMinusConstantAlphaGl: + return BlendingFactor.OneMinusConstantAlpha; + + case GalBlendFactor.SrcAlphaSaturate: + case GalBlendFactor.SrcAlphaSaturateGl: + return BlendingFactor.SrcAlphaSaturate; + + case GalBlendFactor.Src1Color: + case GalBlendFactor.Src1ColorGl: + return BlendingFactor.Src1Color; + + case GalBlendFactor.OneMinusSrc1Color: + case GalBlendFactor.OneMinusSrc1ColorGl: + return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Color; + + case GalBlendFactor.Src1Alpha: + case GalBlendFactor.Src1AlphaGl: + return BlendingFactor.Src1Alpha; + + case GalBlendFactor.OneMinusSrc1Alpha: + case GalBlendFactor.OneMinusSrc1AlphaGl: + return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Alpha; case GalBlendFactor.ConstantColor: - case GalBlendFactor.ConstantColorG80: + case GalBlendFactor.ConstantColorGl: return BlendingFactor.ConstantColor; } - throw new ArgumentException(nameof(BlendFactor)); + throw new ArgumentException(nameof(BlendFactor) + " \"" + BlendFactor + "\" is not valid!"); } } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs index 5ad422980c..11daeb593c 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs @@ -1,40 +1,17 @@ using OpenTK.Graphics.OpenGL; +using System; namespace Ryujinx.Graphics.Gal.OpenGL { static class OGLExtension { - private static bool Initialized = false; + private static Lazy s_EnhancedLayouts = new Lazy(() => HasExtension("GL_ARB_enhanced_layouts")); + private static Lazy s_TextureMirrorClamp = new Lazy(() => HasExtension("GL_EXT_texture_mirror_clamp")); + private static Lazy s_ViewportArray = new Lazy(() => HasExtension("GL_ARB_viewport_array")); - private static bool EnhancedLayouts; - - private static bool TextureMirrorClamp; - - public static bool HasEnhancedLayouts() - { - EnsureInitialized(); - - return EnhancedLayouts; - } - - public static bool HasTextureMirrorClamp() - { - EnsureInitialized(); - - return TextureMirrorClamp; - } - - private static void EnsureInitialized() - { - if (Initialized) - { - return; - } - - EnhancedLayouts = HasExtension("GL_ARB_enhanced_layouts"); - - TextureMirrorClamp = HasExtension("GL_EXT_texture_mirror_clamp"); - } + public static bool EnhancedLayouts => s_EnhancedLayouts.Value; + public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value; + public static bool ViewportArray => s_ViewportArray.Value; private static bool HasExtension(string Name) { diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index 051b105048..e81cf8a384 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -25,6 +25,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GalVertexAttribSize._11_11_10, 3 } }; + private static Dictionary FloatAttribTypes = + new Dictionary() + { + { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float }, + { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Float }, + { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.HalfFloat }, + { GalVertexAttribSize._32_32, VertexAttribPointerType.Float }, + { GalVertexAttribSize._16_16_16, VertexAttribPointerType.HalfFloat }, + { GalVertexAttribSize._16_16, VertexAttribPointerType.HalfFloat }, + { GalVertexAttribSize._32, VertexAttribPointerType.Float }, + { GalVertexAttribSize._16, VertexAttribPointerType.HalfFloat } + }; + private static Dictionary SignedAttribTypes = new Dictionary() { @@ -82,42 +95,44 @@ namespace Ryujinx.Graphics.Gal.OpenGL FrontFace = GalFrontFace.CCW, CullFaceEnabled = false, - CullFace = GalCullFace.Back, + CullFace = GalCullFace.Back, - DepthTestEnabled = false, - DepthFunc = GalComparisonOp.Less, + DepthTestEnabled = false, + DepthWriteEnabled = true, + DepthFunc = GalComparisonOp.Less, + DepthRangeNear = 0, + DepthRangeFar = 1, StencilTestEnabled = false, StencilBackFuncFunc = GalComparisonOp.Always, - StencilBackFuncRef = 0, + StencilBackFuncRef = 0, StencilBackFuncMask = UInt32.MaxValue, - StencilBackOpFail = GalStencilOp.Keep, - StencilBackOpZFail = GalStencilOp.Keep, - StencilBackOpZPass = GalStencilOp.Keep, - StencilBackMask = UInt32.MaxValue, + StencilBackOpFail = GalStencilOp.Keep, + StencilBackOpZFail = GalStencilOp.Keep, + StencilBackOpZPass = GalStencilOp.Keep, + StencilBackMask = UInt32.MaxValue, StencilFrontFuncFunc = GalComparisonOp.Always, - StencilFrontFuncRef = 0, + StencilFrontFuncRef = 0, StencilFrontFuncMask = UInt32.MaxValue, - StencilFrontOpFail = GalStencilOp.Keep, - StencilFrontOpZFail = GalStencilOp.Keep, - StencilFrontOpZPass = GalStencilOp.Keep, - StencilFrontMask = UInt32.MaxValue, + StencilFrontOpFail = GalStencilOp.Keep, + StencilFrontOpZFail = GalStencilOp.Keep, + StencilFrontOpZPass = GalStencilOp.Keep, + StencilFrontMask = UInt32.MaxValue, - BlendEnabled = false, - BlendSeparateAlpha = false, - - BlendEquationRgb = 0, - BlendFuncSrcRgb = GalBlendFactor.One, - BlendFuncDstRgb = GalBlendFactor.Zero, - BlendEquationAlpha = 0, - BlendFuncSrcAlpha = GalBlendFactor.One, - BlendFuncDstAlpha = GalBlendFactor.Zero, + BlendIndependent = false, PrimitiveRestartEnabled = false, - PrimitiveRestartIndex = 0 + PrimitiveRestartIndex = 0 }; + + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + { + Old.Blends[Index] = BlendState.Default; + + Old.ColorMasks[Index] = ColorMaskState.Default; + } } public void Bind(GalPipelineState New) @@ -126,6 +141,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL BindVertexLayout(New); + if (New.FramebufferSrgb != Old.FramebufferSrgb) + { + Enable(EnableCap.FramebufferSrgb, New.FramebufferSrgb); + } + if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY || New.Instance != Old.Instance) { Shader.SetExtraData(New.FlipX, New.FlipY, New.Instance); @@ -133,19 +153,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL //Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved - //if (New.FrontFace != O.FrontFace) + //if (New.FrontFace != Old.FrontFace) //{ // GL.FrontFace(OGLEnumConverter.GetFrontFace(New.FrontFace)); //} - //if (New.CullFaceEnabled != O.CullFaceEnabled) + //if (New.CullFaceEnabled != Old.CullFaceEnabled) //{ // Enable(EnableCap.CullFace, New.CullFaceEnabled); //} //if (New.CullFaceEnabled) //{ - // if (New.CullFace != O.CullFace) + // if (New.CullFace != Old.CullFace) // { // GL.CullFace(OGLEnumConverter.GetCullFace(New.CullFace)); // } @@ -156,6 +176,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL Enable(EnableCap.DepthTest, New.DepthTestEnabled); } + if (New.DepthWriteEnabled != Old.DepthWriteEnabled) + { + GL.DepthMask(New.DepthWriteEnabled); + } + if (New.DepthTestEnabled) { if (New.DepthFunc != Old.DepthFunc) @@ -164,11 +189,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + if (New.DepthRangeNear != Old.DepthRangeNear || + New.DepthRangeFar != Old.DepthRangeFar) + { + GL.DepthRange(New.DepthRangeNear, New.DepthRangeFar); + } + if (New.StencilTestEnabled != Old.StencilTestEnabled) { Enable(EnableCap.StencilTest, New.StencilTestEnabled); } + if (New.StencilTwoSideEnabled != Old.StencilTwoSideEnabled) + { + Enable((EnableCap)All.StencilTestTwoSideExt, New.StencilTwoSideEnabled); + } + if (New.StencilTestEnabled) { if (New.StencilBackFuncFunc != Old.StencilBackFuncFunc || @@ -226,48 +262,48 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - if (New.BlendEnabled != Old.BlendEnabled) + if (New.BlendIndependent) { - Enable(EnableCap.Blend, New.BlendEnabled); - } - - if (New.BlendEnabled) - { - if (New.BlendSeparateAlpha) + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) { - if (New.BlendEquationRgb != Old.BlendEquationRgb || - New.BlendEquationAlpha != Old.BlendEquationAlpha) - { - GL.BlendEquationSeparate( - OGLEnumConverter.GetBlendEquation(New.BlendEquationRgb), - OGLEnumConverter.GetBlendEquation(New.BlendEquationAlpha)); - } - - if (New.BlendFuncSrcRgb != Old.BlendFuncSrcRgb || - New.BlendFuncDstRgb != Old.BlendFuncDstRgb || - New.BlendFuncSrcAlpha != Old.BlendFuncSrcAlpha || - New.BlendFuncDstAlpha != Old.BlendFuncDstAlpha) - { - GL.BlendFuncSeparate( - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcRgb), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.BlendFuncDstRgb), - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcAlpha), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.BlendFuncDstAlpha)); - } + SetBlendState(Index, New.Blends[Index], Old.Blends[Index]); + } + } + else + { + if (New.BlendIndependent != Old.BlendIndependent) + { + SetAllBlendState(New.Blends[0]); } else { - if (New.BlendEquationRgb != Old.BlendEquationRgb) - { - GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.BlendEquationRgb)); - } + SetBlendState(New.Blends[0], Old.Blends[0]); + } + } - if (New.BlendFuncSrcRgb != Old.BlendFuncSrcRgb || - New.BlendFuncDstRgb != Old.BlendFuncDstRgb) + if (New.ColorMaskCommon) + { + if (New.ColorMaskCommon != Old.ColorMaskCommon || !New.ColorMasks[0].Equals(Old.ColorMasks[0])) + { + GL.ColorMask( + New.ColorMasks[0].Red, + New.ColorMasks[0].Green, + New.ColorMasks[0].Blue, + New.ColorMasks[0].Alpha); + } + } + else + { + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + { + if (!New.ColorMasks[Index].Equals(Old.ColorMasks[Index])) { - GL.BlendFunc( - OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcRgb), - OGLEnumConverter.GetBlendFactor(New.BlendFuncDstRgb)); + GL.ColorMask( + Index, + New.ColorMasks[Index].Red, + New.ColorMasks[Index].Green, + New.ColorMasks[Index].Blue, + New.ColorMasks[Index].Alpha); } } } @@ -288,6 +324,136 @@ namespace Ryujinx.Graphics.Gal.OpenGL Old = New; } + private void SetAllBlendState(BlendState New) + { + Enable(EnableCap.Blend, New.Enabled); + + if (New.Enabled) + { + if (New.SeparateAlpha) + { + GL.BlendEquationSeparate( + OGLEnumConverter.GetBlendEquation(New.EquationRgb), + OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + + GL.BlendFuncSeparate( + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + } + else + { + GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + + GL.BlendFunc( + OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + } + } + } + + private void SetBlendState(BlendState New, BlendState Old) + { + if (New.Enabled != Old.Enabled) + { + Enable(EnableCap.Blend, New.Enabled); + } + + if (New.Enabled) + { + if (New.SeparateAlpha) + { + if (New.EquationRgb != Old.EquationRgb || + New.EquationAlpha != Old.EquationAlpha) + { + GL.BlendEquationSeparate( + OGLEnumConverter.GetBlendEquation(New.EquationRgb), + OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + } + + if (New.FuncSrcRgb != Old.FuncSrcRgb || + New.FuncDstRgb != Old.FuncDstRgb || + New.FuncSrcAlpha != Old.FuncSrcAlpha || + New.FuncDstAlpha != Old.FuncDstAlpha) + { + GL.BlendFuncSeparate( + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + } + } + else + { + if (New.EquationRgb != Old.EquationRgb) + { + GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + } + + if (New.FuncSrcRgb != Old.FuncSrcRgb || + New.FuncDstRgb != Old.FuncDstRgb) + { + GL.BlendFunc( + OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + } + } + } + } + + private void SetBlendState(int Index, BlendState New, BlendState Old) + { + if (New.Enabled != Old.Enabled) + { + Enable(IndexedEnableCap.Blend, Index, New.Enabled); + } + + if (New.Enabled) + { + if (New.SeparateAlpha) + { + if (New.EquationRgb != Old.EquationRgb || + New.EquationAlpha != Old.EquationAlpha) + { + GL.BlendEquationSeparate( + Index, + OGLEnumConverter.GetBlendEquation(New.EquationRgb), + OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + } + + if (New.FuncSrcRgb != Old.FuncSrcRgb || + New.FuncDstRgb != Old.FuncDstRgb || + New.FuncSrcAlpha != Old.FuncSrcAlpha || + New.FuncDstAlpha != Old.FuncDstAlpha) + { + GL.BlendFuncSeparate( + Index, + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + } + } + else + { + if (New.EquationRgb != Old.EquationRgb) + { + GL.BlendEquation(Index, OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + } + + if (New.FuncSrcRgb != Old.FuncSrcRgb || + New.FuncDstRgb != Old.FuncDstRgb) + { + GL.BlendFunc( + Index, + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + } + } + } + } + private void BindConstBuffers(GalPipelineState New) { int FreeBinding = OGLShader.ReservedCbufCount; @@ -337,7 +503,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL foreach (GalVertexAttrib Attrib in Binding.Attribs) { - GL.EnableVertexAttribArray(Attrib.Index); + //Skip uninitialized attributes. + if (Attrib.Size == 0) + { + continue; + } GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); @@ -354,35 +524,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (Attrib.Type == GalVertexAttribType.Float) { - Type = VertexAttribPointerType.Float; + Type = GetType(FloatAttribTypes, Attrib); } else { if (Unsigned) { - Type = UnsignedAttribTypes[Attrib.Size]; + Type = GetType(UnsignedAttribTypes, Attrib); } else { - Type = SignedAttribTypes[Attrib.Size]; + Type = GetType(SignedAttribTypes, Attrib); } } - int Size = AttribElements[Attrib.Size]; + if (!AttribElements.TryGetValue(Attrib.Size, out int Size)) + { + throw new InvalidOperationException("Invalid attribute size \"" + Attrib.Size + "\"!"); + } + int Offset = Attrib.Offset; - if (Attrib.Type == GalVertexAttribType.Sint || - Attrib.Type == GalVertexAttribType.Uint) + if (Binding.Stride != 0) { - IntPtr Pointer = new IntPtr(Offset); + GL.EnableVertexAttribArray(Attrib.Index); - VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + if (Attrib.Type == GalVertexAttribType.Sint || + Attrib.Type == GalVertexAttribType.Uint) + { + IntPtr Pointer = new IntPtr(Offset); - GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer); + VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + + GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer); + } + else + { + GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset); + } } else { - GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset); + GL.DisableVertexAttribArray(Attrib.Index); + + SetConstAttrib(Attrib); } if (Binding.Instanced && Binding.Divisor != 0) @@ -397,6 +582,149 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + private static VertexAttribPointerType GetType(Dictionary Dict, GalVertexAttrib Attrib) + { + if (!Dict.TryGetValue(Attrib.Size, out VertexAttribPointerType Type)) + { + ThrowUnsupportedAttrib(Attrib); + } + + return Type; + } + + private unsafe static void SetConstAttrib(GalVertexAttrib Attrib) + { + if (Attrib.Size == GalVertexAttribSize._10_10_10_2 || + Attrib.Size == GalVertexAttribSize._11_11_10) + { + ThrowUnsupportedAttrib(Attrib); + } + + if (Attrib.Type == GalVertexAttribType.Unorm) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer); + break; + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Attrib.Pointer); + break; + + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Attrib.Pointer); + break; + } + } + else if (Attrib.Type == GalVertexAttribType.Snorm) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer); + break; + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + GL.VertexAttrib4N((uint)Attrib.Index, (short*)Attrib.Pointer); + break; + + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttrib4N((uint)Attrib.Index, (int*)Attrib.Pointer); + break; + } + } + else if (Attrib.Type == GalVertexAttribType.Uint) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer); + break; + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Attrib.Pointer); + break; + + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttribI4((uint)Attrib.Index, (uint*)Attrib.Pointer); + break; + } + } + else if (Attrib.Type == GalVertexAttribType.Sint) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer); + break; + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + GL.VertexAttribI4((uint)Attrib.Index, (short*)Attrib.Pointer); + break; + + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttribI4((uint)Attrib.Index, (int*)Attrib.Pointer); + break; + } + } + else if (Attrib.Type == GalVertexAttribType.Float) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer); + break; + + default: ThrowUnsupportedAttrib(Attrib); break; + } + } + } + + private static void ThrowUnsupportedAttrib(GalVertexAttrib Attrib) + { + throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!"); + } + private void Enable(EnableCap Cap, bool Enabled) { if (Enabled) @@ -408,5 +736,27 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Disable(Cap); } } + + private void Enable(IndexedEnableCap Cap, int Index, bool Enabled) + { + if (Enabled) + { + GL.Enable(Cap, Index); + } + else + { + GL.Disable(Cap, Index); + } + } + + public void ResetDepthMask() + { + Old.DepthWriteEnabled = true; + } + + public void ResetColorMask(int Index) + { + Old.ColorMasks[Index] = ColorMaskState.Default; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 4510669280..cd6292f7e7 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -5,6 +5,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL { class OGLRasterizer : IGalRasterizer { + private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024; + private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024; + private int[] VertexBuffers; private OGLCachedResource VboCache; @@ -24,8 +27,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL { VertexBuffers = new int[32]; - VboCache = new OGLCachedResource(GL.DeleteBuffer); - IboCache = new OGLCachedResource(GL.DeleteBuffer); + VboCache = new OGLCachedResource(GL.DeleteBuffer, MaxVertexBufferCacheSize); + IboCache = new OGLCachedResource(GL.DeleteBuffer, MaxIndexBufferCacheSize); IndexBuffer = new IbInfo(); } @@ -45,11 +48,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void ClearBuffers( GalClearBufferFlags Flags, int Attachment, - float Red, float Green, float Blue, float Alpha, + float Red, + float Green, + float Blue, + float Alpha, float Depth, int Stencil) { GL.ColorMask( + Attachment, Flags.HasFlag(GalClearBufferFlags.ColorRed), Flags.HasFlag(GalClearBufferFlags.ColorGreen), Flags.HasFlag(GalClearBufferFlags.ColorBlue), @@ -57,6 +64,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.ClearBuffer(ClearBuffer.Color, Attachment, new float[] { Red, Green, Blue, Alpha }); + GL.ColorMask(Attachment, true, true, true, true); + GL.DepthMask(true); + if (Flags.HasFlag(GalClearBufferFlags.Depth)) { GL.ClearBuffer(ClearBuffer.Depth, 0, ref Depth); @@ -66,8 +76,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GL.ClearBuffer(ClearBuffer.Stencil, 0, ref Stencil); } - - GL.ColorMask(true, true, true, true); } public bool IsVboCached(long Key, long DataSize) @@ -104,6 +112,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw); } + public void CreateIbo(long Key, int DataSize, byte[] Buffer) + { + int Handle = GL.GenBuffer(); + + IboCache.AddOrUpdate(Key, Handle, (uint)DataSize); + + IntPtr Length = new IntPtr(Buffer.Length); + + GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle); + GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); + } + public void SetIndexArray(int Size, GalIndexFormat Format) { IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format); @@ -113,14 +133,33 @@ namespace Ryujinx.Graphics.Gal.OpenGL IndexBuffer.ElemSizeLog2 = (int)Format; } - public void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType) + public void DrawArrays(int First, int Count, GalPrimitiveType PrimType) { - if (PrimCount == 0) + if (Count == 0) { return; } - GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount); + if (PrimType == GalPrimitiveType.Quads) + { + for (int Offset = 0; Offset < Count; Offset += 4) + { + GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4); + } + } + else if (PrimType == GalPrimitiveType.QuadStrip) + { + GL.DrawArrays(PrimitiveType.TriangleFan, First, 4); + + for (int Offset = 2; Offset < Count; Offset += 2) + { + GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4); + } + } + else + { + GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count); + } } public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs index 99bfa350de..8d04f1aae7 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs @@ -6,6 +6,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL { class OGLRenderTarget : IGalRenderTarget { + private const int NativeWidth = 1280; + private const int NativeHeight = 720; + + private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount; + private struct Rect { public int X { get; private set; } @@ -15,26 +20,55 @@ namespace Ryujinx.Graphics.Gal.OpenGL public Rect(int X, int Y, int Width, int Height) { - this.X = X; - this.Y = Y; - this.Width = Width; + this.X = X; + this.Y = Y; + this.Width = Width; this.Height = Height; } } - private const int NativeWidth = 1280; - private const int NativeHeight = 720; + private class FrameBufferAttachments + { + public int MapCount { get; set; } - private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; + public DrawBuffersEnum[] Map { get; private set; } + + public long[] Colors { get; private set; } + + public long Zeta { get; set; } + + public FrameBufferAttachments() + { + Colors = new long[RenderTargetsCount]; + + Map = new DrawBuffersEnum[RenderTargetsCount]; + } + + public void Update(FrameBufferAttachments Source) + { + for (int Index = 0; Index < RenderTargetsCount; Index++) + { + Map[Index] = Source.Map[Index]; + + Colors[Index] = Source.Colors[Index]; + } + + MapCount = Source.MapCount; + Zeta = Source.Zeta; + } + } + + private int[] ColorHandles; + private int ZetaHandle; private OGLTexture Texture; - private ImageHandler RawTex; private ImageHandler ReadTex; - private Rect Viewport; private Rect Window; + private float[] Viewports; + private bool FlipX; private bool FlipY; @@ -51,91 +85,115 @@ namespace Ryujinx.Graphics.Gal.OpenGL private int SrcFb; private int DstFb; - //Holds current attachments, used to avoid unnecesary calls to OpenGL - private int[] ColorAttachments; + private FrameBufferAttachments Attachments; + private FrameBufferAttachments OldAttachments; - private int DepthAttachment; - private int StencilAttachment; + private int CopyPBO; public OGLRenderTarget(OGLTexture Texture) { - ColorAttachments = new int[8]; + Attachments = new FrameBufferAttachments(); + + OldAttachments = new FrameBufferAttachments(); + + ColorHandles = new int[RenderTargetsCount]; + + Viewports = new float[RenderTargetsCount * 4]; this.Texture = Texture; + + Texture.TextureDeleted += TextureDeletionHandler; } - public void BindColor(long Key, int Attachment) + private void TextureDeletionHandler(object Sender, int Handle) { - if (Texture.TryGetImage(Key, out ImageHandler Tex)) + //Texture was deleted, the handle is no longer valid, so + //reset all uses of this handle on a render target. + for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++) { - EnsureFrameBuffer(); - - Attach(ref ColorAttachments[Attachment], Tex.Handle, FramebufferAttachment.ColorAttachment0 + Attachment); - } - else - { - UnbindColor(Attachment); - } - } - - public void UnbindColor(int Attachment) - { - EnsureFrameBuffer(); - - Attach(ref ColorAttachments[Attachment], 0, FramebufferAttachment.ColorAttachment0 + Attachment); - } - - public void BindZeta(long Key) - { - if (Texture.TryGetImage(Key, out ImageHandler Tex)) - { - EnsureFrameBuffer(); - - if (Tex.HasDepth && Tex.HasStencil) + if (ColorHandles[Attachment] == Handle) { - if (DepthAttachment != Tex.Handle || - StencilAttachment != Tex.Handle) + ColorHandles[Attachment] = 0; + } + } + + if (ZetaHandle == Handle) + { + ZetaHandle = 0; + } + } + + public void Bind() + { + if (DummyFrameBuffer == 0) + { + DummyFrameBuffer = GL.GenFramebuffer(); + } + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer); + + ImageHandler CachedImage; + + for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++) + { + long Key = Attachments.Colors[Attachment]; + + int Handle = 0; + + if (Key != 0 && Texture.TryGetImageHandler(Key, out CachedImage)) + { + Handle = CachedImage.Handle; + } + + if (Handle == ColorHandles[Attachment]) + { + continue; + } + + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.ColorAttachment0 + Attachment, + Handle, + 0); + + ColorHandles[Attachment] = Handle; + } + + if (Attachments.Zeta != 0 && Texture.TryGetImageHandler(Attachments.Zeta, out CachedImage)) + { + if (CachedImage.Handle != ZetaHandle) + { + if (CachedImage.HasDepth && CachedImage.HasStencil) { GL.FramebufferTexture( FramebufferTarget.DrawFramebuffer, FramebufferAttachment.DepthStencilAttachment, - Tex.Handle, + CachedImage.Handle, + 0); + } + else if (CachedImage.HasDepth) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthAttachment, + CachedImage.Handle, 0); - DepthAttachment = Tex.Handle; - - StencilAttachment = Tex.Handle; + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.StencilAttachment, + 0, + 0); + } + else + { + throw new InvalidOperationException("Invalid image format \"" + CachedImage.Format + "\" used as Zeta!"); } - } - else if (Tex.HasDepth) - { - Attach(ref DepthAttachment, Tex.Handle, FramebufferAttachment.DepthAttachment); - Attach(ref StencilAttachment, 0, FramebufferAttachment.StencilAttachment); - } - else if (Tex.HasStencil) - { - Attach(ref DepthAttachment, 0, FramebufferAttachment.DepthAttachment); - - Attach(ref StencilAttachment, Tex.Handle, FramebufferAttachment.StencilAttachment); - } - else - { - throw new InvalidOperationException(); + ZetaHandle = CachedImage.Handle; } } - else - { - UnbindZeta(); - } - } - - public void UnbindZeta() - { - EnsureFrameBuffer(); - - if (DepthAttachment != 0 || - StencilAttachment != 0) + else if (ZetaHandle != 0) { GL.FramebufferTexture( FramebufferTarget.DrawFramebuffer, @@ -143,62 +201,77 @@ namespace Ryujinx.Graphics.Gal.OpenGL 0, 0); - DepthAttachment = 0; - - StencilAttachment = 0; + ZetaHandle = 0; } + + if (OGLExtension.ViewportArray) + { + GL.ViewportArray(0, RenderTargetsCount, Viewports); + } + else + { + GL.Viewport( + (int)Viewports[0], + (int)Viewports[1], + (int)Viewports[2], + (int)Viewports[3]); + } + + if (Attachments.MapCount > 1) + { + GL.DrawBuffers(Attachments.MapCount, Attachments.Map); + } + else if (Attachments.MapCount == 1) + { + GL.DrawBuffer((DrawBufferMode)Attachments.Map[0]); + } + else + { + GL.DrawBuffer(DrawBufferMode.None); + } + + OldAttachments.Update(Attachments); } - public void BindTexture(long Key, int Index) + public void BindColor(long Key, int Attachment) { - if (Texture.TryGetImage(Key, out ImageHandler Tex)) - { - GL.ActiveTexture(TextureUnit.Texture0 + Index); - - GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); - } + Attachments.Colors[Attachment] = Key; } - public void Set(long Key) + public void UnbindColor(int Attachment) { - if (Texture.TryGetImage(Key, out ImageHandler Tex)) - { - ReadTex = Tex; - } + Attachments.Colors[Attachment] = 0; } - public void Set(byte[] Data, int Width, int Height) + public void BindZeta(long Key) { - if (RawTex == null) - { - RawTex = new ImageHandler(); - } + Attachments.Zeta = Key; + } - RawTex.EnsureSetup(new GalImage(Width, Height, RawFormat)); + public void UnbindZeta() + { + Attachments.Zeta = 0; + } - GL.BindTexture(TextureTarget.Texture2D, RawTex.Handle); - - GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, RawTex.PixelFormat, RawTex.PixelType, Data); - - ReadTex = RawTex; + public void Present(long Key) + { + Texture.TryGetImageHandler(Key, out ReadTex); } public void SetMap(int[] Map) { - if (Map != null && Map.Length > 0) + if (Map != null) { - DrawBuffersEnum[] Mode = new DrawBuffersEnum[Map.Length]; + Attachments.MapCount = Map.Length; - for (int i = 0; i < Map.Length; i++) + for (int Attachment = 0; Attachment < Attachments.MapCount; Attachment++) { - Mode[i] = DrawBuffersEnum.ColorAttachment0 + Map[i]; + Attachments.Map[Attachment] = DrawBuffersEnum.ColorAttachment0 + Map[Attachment]; } - - GL.DrawBuffers(Mode.Length, Mode); } else { - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + Attachments.MapCount = 0; } } @@ -218,20 +291,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL Window = new Rect(0, 0, Width, Height); } - public void SetViewport(int X, int Y, int Width, int Height) + public void SetViewport(int Attachment, int X, int Y, int Width, int Height) { - Viewport = new Rect(X, Y, Width, Height); + int Offset = Attachment * 4; - SetViewport(Viewport); - } - - private void SetViewport(Rect Viewport) - { - GL.Viewport( - Viewport.X, - Viewport.Y, - Viewport.Width, - Viewport.Height); + Viewports[Offset + 0] = X; + Viewports[Offset + 1] = Y; + Viewports[Offset + 2] = Width; + Viewports[Offset + 3] = Height; } public void Render() @@ -280,27 +347,27 @@ namespace Ryujinx.Graphics.Gal.OpenGL int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY; int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY; - if (SrcFb == 0) SrcFb = GL.GenFramebuffer(); - - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); - GL.Viewport(0, 0, Window.Width, Window.Height); + if (SrcFb == 0) + { + SrcFb = GL.GenFramebuffer(); + } + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0); GL.ReadBuffer(ReadBufferMode.ColorAttachment0); - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); GL.Clear(ClearBufferMask.ColorBufferBit); GL.BlitFramebuffer( SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, - ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); - - EnsureFrameBuffer(); + ClearBufferMask.ColorBufferBit, + BlitFramebufferFilter.Linear); } public void Copy( @@ -315,183 +382,118 @@ namespace Ryujinx.Graphics.Gal.OpenGL int DstX1, int DstY1) { - if (Texture.TryGetImage(SrcKey, out ImageHandler SrcTex) && - Texture.TryGetImage(DstKey, out ImageHandler DstTex)) + if (Texture.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) && + Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex)) { - if (SrcTex.HasColor != DstTex.HasColor || - SrcTex.HasDepth != DstTex.HasDepth || + if (SrcTex.HasColor != DstTex.HasColor || + SrcTex.HasDepth != DstTex.HasDepth || SrcTex.HasStencil != DstTex.HasStencil) { throw new NotImplementedException(); } + if (SrcFb == 0) + { + SrcFb = GL.GenFramebuffer(); + } + + if (DstFb == 0) + { + DstFb = GL.GenFramebuffer(); + } + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb); + + FramebufferAttachment Attachment = GetAttachment(SrcTex); + + GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0); + GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0); + + BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest; + if (SrcTex.HasColor) { - CopyTextures( - SrcX0, SrcY0, SrcX1, SrcY1, - DstX0, DstY0, DstX1, DstY1, - SrcTex.Handle, - DstTex.Handle, - FramebufferAttachment.ColorAttachment0, - ClearBufferMask.ColorBufferBit, - true); - } - else if (SrcTex.HasDepth && SrcTex.HasStencil) - { - CopyTextures( - SrcX0, SrcY0, SrcX1, SrcY1, - DstX0, DstY0, DstX1, DstY1, - SrcTex.Handle, - DstTex.Handle, - FramebufferAttachment.DepthStencilAttachment, - ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit, - false); - } - else if (SrcTex.HasDepth) - { - CopyTextures( - SrcX0, SrcY0, SrcX1, SrcY1, - DstX0, DstY0, DstX1, DstY1, - SrcTex.Handle, - DstTex.Handle, - FramebufferAttachment.DepthAttachment, - ClearBufferMask.DepthBufferBit, - false); - } - else if (SrcTex.HasStencil) - { - CopyTextures( - SrcX0, SrcY0, SrcX1, SrcY1, - DstX0, DstY0, DstX1, DstY1, - SrcTex.Handle, - DstTex.Handle, - FramebufferAttachment.StencilAttachment, - ClearBufferMask.StencilBufferBit, - false); - } - else - { - throw new InvalidOperationException(); + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + + Filter = BlitFramebufferFilter.Linear; } + + ClearBufferMask Mask = GetClearMask(SrcTex); + + GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter); } } - public void GetBufferData(long Key, Action Callback) + public void Reinterpret(long Key, GalImage NewImage) { - if (Texture.TryGetImage(Key, out ImageHandler Tex)) + if (!Texture.TryGetImage(Key, out GalImage OldImage)) { - byte[] Data = new byte[ImageUtils.GetSize(Tex.Image)]; + return; + } - GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); + if (NewImage.Format == OldImage.Format) + { + return; + } - GL.GetTexImage( - TextureTarget.Texture2D, - 0, - Tex.PixelFormat, - Tex.PixelType, - Data); + if (CopyPBO == 0) + { + CopyPBO = GL.GenBuffer(); + } - Callback(Data); + GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyPBO); + + GL.BufferData(BufferTarget.PixelPackBuffer, Math.Max(ImageUtils.GetSize(OldImage), ImageUtils.GetSize(NewImage)), IntPtr.Zero, BufferUsageHint.StreamCopy); + + if (!Texture.TryGetImageHandler(Key, out ImageHandler CachedImage)) + { + throw new InvalidOperationException(); + } + + (_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format); + + GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle); + + GL.GetTexImage(TextureTarget.Texture2D, 0, Format, Type, IntPtr.Zero); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO); + + Texture.Create(Key, ImageUtils.GetSize(NewImage), NewImage); + + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); + } + + private static FramebufferAttachment GetAttachment(ImageHandler CachedImage) + { + if (CachedImage.HasColor) + { + return FramebufferAttachment.ColorAttachment0; + } + else if (CachedImage.HasDepth && CachedImage.HasStencil) + { + return FramebufferAttachment.DepthStencilAttachment; + } + else if (CachedImage.HasDepth) + { + return FramebufferAttachment.DepthAttachment; + } + else if (CachedImage.HasStencil) + { + return FramebufferAttachment.StencilAttachment; + } + else + { + throw new InvalidOperationException(); } } - public void SetBufferData( - long Key, - int Width, - int Height, - byte[] Buffer) + private static ClearBufferMask GetClearMask(ImageHandler CachedImage) { - if (Texture.TryGetImage(Key, out ImageHandler Tex)) - { - GL.BindTexture(TextureTarget.Texture2D, Tex.Handle); - - const int Level = 0; - const int Border = 0; - - GL.TexImage2D( - TextureTarget.Texture2D, - Level, - Tex.InternalFormat, - Width, - Height, - Border, - Tex.PixelFormat, - Tex.PixelType, - Buffer); - } - } - - private void EnsureFrameBuffer() - { - if (DummyFrameBuffer == 0) - { - DummyFrameBuffer = GL.GenFramebuffer(); - } - - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer); - } - - private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment) - { - if (OldHandle != NewHandle) - { - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FbAttachment, - NewHandle, - 0); - - OldHandle = NewHandle; - } - } - - private void CopyTextures( - int SrcX0, - int SrcY0, - int SrcX1, - int SrcY1, - int DstX0, - int DstY0, - int DstX1, - int DstY1, - int SrcTexture, - int DstTexture, - FramebufferAttachment Attachment, - ClearBufferMask Mask, - bool Color) - { - if (SrcFb == 0) SrcFb = GL.GenFramebuffer(); - if (DstFb == 0) DstFb = GL.GenFramebuffer(); - - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb); - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb); - - GL.FramebufferTexture( - FramebufferTarget.ReadFramebuffer, - Attachment, - SrcTexture, - 0); - - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - Attachment, - DstTexture, - 0); - - if (Color) - { - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); - } - - GL.Clear(Mask); - - GL.BlitFramebuffer( - SrcX0, SrcY0, SrcX1, SrcY1, - DstX0, DstY0, DstX1, DstY1, - Mask, - Color ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest); - - EnsureFrameBuffer(); + return (CachedImage.HasColor ? ClearBufferMask.ColorBufferBit : 0) | + (CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) | + (CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 73d37b8791..b45a3a3a5a 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -55,16 +55,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL GlslDecompiler Decompiler = new GlslDecompiler(); + int ShaderDumpIndex = ShaderDumper.DumpIndex; + if (IsDualVp) { ShaderDumper.Dump(Memory, Position, Type, "a"); ShaderDumper.Dump(Memory, PositionB, Type, "b"); - Program = Decompiler.Decompile( - Memory, - Position, - PositionB, - Type); + Program = Decompiler.Decompile(Memory, Position, PositionB, Type); } else { @@ -73,11 +71,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL Program = Decompiler.Decompile(Memory, Position, Type); } - return new OGLShaderStage( - Type, - Program.Code, - Program.Uniforms, - Program.Textures); + string Code = Program.Code; + + if (ShaderDumper.IsDumpEnabled()) + { + Code = "//Shader " + ShaderDumpIndex + Environment.NewLine + Code; + } + + return new OGLShaderStage(Type, Code, Program.Uniforms, Program.Textures); } public IEnumerable GetConstBufferUsage(long Key) @@ -133,7 +134,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { //Enhanced layouts are required for Geometry shaders //skip this stage if current driver has no ARB_enhanced_layouts - if (!OGLExtension.HasEnhancedLayouts()) + if (!OGLExtension.EnhancedLayouts) { return; } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 82f9c9139c..ef984b1ed3 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -6,11 +6,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL { class OGLTexture : IGalTexture { + private const long MaxTextureCacheSize = 768 * 1024 * 1024; + private OGLCachedResource TextureCache; + public EventHandler TextureDeleted { get; set; } + public OGLTexture() { - TextureCache = new OGLCachedResource(DeleteTexture); + TextureCache = new OGLCachedResource(DeleteTexture, MaxTextureCacheSize); } public void LockCache() @@ -23,27 +27,57 @@ namespace Ryujinx.Graphics.Gal.OpenGL TextureCache.Unlock(); } - private static void DeleteTexture(ImageHandler CachedImage) + private void DeleteTexture(ImageHandler CachedImage) { + TextureDeleted?.Invoke(this, CachedImage.Handle); + GL.DeleteTexture(CachedImage.Handle); } - public void Create(long Key, byte[] Data, GalImage Image) + public void Create(long Key, int Size, GalImage Image) { int Handle = GL.GenTexture(); - TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length); - GL.BindTexture(TextureTarget.Texture2D, Handle); const int Level = 0; //TODO: Support mipmap textures. const int Border = 0; - GalImageFormat TypeLess = Image.Format & GalImageFormat.FormatMask; + TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Size); - bool IsASTC = TypeLess >= GalImageFormat.ASTC_BEGIN && TypeLess <= GalImageFormat.ASTC_END; + if (ImageUtils.IsCompressed(Image.Format)) + { + throw new InvalidOperationException("Surfaces with compressed formats are not supported!"); + } - if (ImageUtils.IsCompressed(Image.Format) && !IsASTC) + (PixelInternalFormat InternalFmt, + PixelFormat Format, + PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); + + GL.TexImage2D( + TextureTarget.Texture2D, + Level, + InternalFmt, + Image.Width, + Image.Height, + Border, + Format, + Type, + IntPtr.Zero); + } + + public void Create(long Key, byte[] Data, GalImage Image) + { + int Handle = GL.GenTexture(); + + GL.BindTexture(TextureTarget.Texture2D, Handle); + + const int Level = 0; //TODO: Support mipmap textures. + const int Border = 0; + + TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length); + + if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format)) { InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format); @@ -60,10 +94,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL else { //TODO: Use KHR_texture_compression_astc_hdr when available - if (IsASTC) + if (IsAstc(Image.Format)) { - int TextureBlockWidth = GetAstcBlockWidth(Image.Format); - int TextureBlockHeight = GetAstcBlockHeight(Image.Format); + int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format); + int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format); Data = ASTCDecoder.DecodeToRGBA8888( Data, @@ -72,25 +106,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL Image.Width, Image.Height, 1); - Image.Format = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; - } - else if (TypeLess == GalImageFormat.G8R8) - { - Data = ImageConverter.G8R8ToR8G8( - Data, - Image.Width, - Image.Height, - 1); - - Image.Format = GalImageFormat.R8G8 | (Image.Format & GalImageFormat.FormatMask); + Image.Format = GalImageFormat.RGBA8 | (Image.Format & GalImageFormat.TypeMask); } - (PixelInternalFormat InternalFormat, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); + (PixelInternalFormat InternalFmt, + PixelFormat Format, + PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); GL.TexImage2D( TextureTarget.Texture2D, Level, - InternalFormat, + InternalFmt, Image.Width, Image.Height, Border, @@ -98,31 +124,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL Type, Data); } - - int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Image.XSource); - int SwizzleG = (int)OGLEnumConverter.GetTextureSwizzle(Image.YSource); - int SwizzleB = (int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource); - int SwizzleA = (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, SwizzleR); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, SwizzleG); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, SwizzleB); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA); } - public void CreateFb(long Key, long Size, GalImage Image) + private static bool IsAstc(GalImageFormat Format) { - if (!TryGetImage(Key, out ImageHandler CachedImage)) - { - CachedImage = new ImageHandler(); + Format &= GalImageFormat.FormatMask; - TextureCache.AddOrUpdate(Key, CachedImage, Size); + return Format > GalImageFormat.Astc2DStart && Format < GalImageFormat.Astc2DEnd; + } + + public bool TryGetImage(long Key, out GalImage Image) + { + if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage)) + { + Image = CachedImage.Image; + + return true; } - CachedImage.EnsureSetup(Image); + Image = default(GalImage); + + return false; } - public bool TryGetImage(long Key, out ImageHandler CachedImage) + public bool TryGetImageHandler(long Key, out ImageHandler CachedImage) { if (TextureCache.TryGetValue(Key, out CachedImage)) { @@ -134,76 +159,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL return false; } - private static int GetAstcBlockWidth(GalImageFormat Format) - { - switch (Format) - { - case GalImageFormat.ASTC_4x4 | GalImageFormat.Unorm: return 4; - case GalImageFormat.ASTC_5x5 | GalImageFormat.Unorm: return 5; - case GalImageFormat.ASTC_6x6 | GalImageFormat.Unorm: return 6; - case GalImageFormat.ASTC_8x8 | GalImageFormat.Unorm: return 8; - case GalImageFormat.ASTC_10x10 | GalImageFormat.Unorm: return 10; - case GalImageFormat.ASTC_12x12 | GalImageFormat.Unorm: return 12; - case GalImageFormat.ASTC_5x4 | GalImageFormat.Unorm: return 5; - case GalImageFormat.ASTC_6x5 | GalImageFormat.Unorm: return 6; - case GalImageFormat.ASTC_8x6 | GalImageFormat.Unorm: return 8; - case GalImageFormat.ASTC_10x8 | GalImageFormat.Unorm: return 10; - case GalImageFormat.ASTC_12x10 | GalImageFormat.Unorm: return 12; - case GalImageFormat.ASTC_8x5 | GalImageFormat.Unorm: return 8; - case GalImageFormat.ASTC_10x5 | GalImageFormat.Unorm: return 10; - case GalImageFormat.ASTC_10x6 | GalImageFormat.Unorm: return 10; - } - - throw new ArgumentException(nameof(Format)); - } - - private static int GetAstcBlockHeight(GalImageFormat Format) - { - switch (Format) - { - case GalImageFormat.ASTC_4x4 | GalImageFormat.Unorm: return 4; - case GalImageFormat.ASTC_5x5 | GalImageFormat.Unorm: return 5; - case GalImageFormat.ASTC_6x6 | GalImageFormat.Unorm: return 6; - case GalImageFormat.ASTC_8x8 | GalImageFormat.Unorm: return 8; - case GalImageFormat.ASTC_10x10 | GalImageFormat.Unorm: return 10; - case GalImageFormat.ASTC_12x12 | GalImageFormat.Unorm: return 12; - case GalImageFormat.ASTC_5x4 | GalImageFormat.Unorm: return 4; - case GalImageFormat.ASTC_6x5 | GalImageFormat.Unorm: return 5; - case GalImageFormat.ASTC_8x6 | GalImageFormat.Unorm: return 6; - case GalImageFormat.ASTC_10x8 | GalImageFormat.Unorm: return 8; - case GalImageFormat.ASTC_12x10 | GalImageFormat.Unorm: return 10; - case GalImageFormat.ASTC_8x5 | GalImageFormat.Unorm: return 5; - case GalImageFormat.ASTC_10x5 | GalImageFormat.Unorm: return 5; - case GalImageFormat.ASTC_10x6 | GalImageFormat.Unorm: return 6; - } - - throw new ArgumentException(nameof(Format)); - } - - public bool TryGetCachedTexture(long Key, long DataSize, out GalImage Image) - { - if (TextureCache.TryGetSize(Key, out long Size) && Size == DataSize) - { - if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage)) - { - Image = CachedImage.Image; - - return true; - } - } - - Image = default(GalImage); - - return false; - } - - public void Bind(long Key, int Index) + public void Bind(long Key, int Index, GalImage Image) { if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage)) { GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle); + + int[] SwizzleRgba = new int[] + { + (int)OGLEnumConverter.GetTextureSwizzle(Image.XSource), + (int)OGLEnumConverter.GetTextureSwizzle(Image.YSource), + (int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource), + (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource) + }; + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, SwizzleRgba); } } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index ac34400e38..5b62ac3a6e 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -252,10 +252,9 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine(IdentationStr + "int " + GlslDecl.InstanceUniformName + ";"); SB.AppendLine("};"); + SB.AppendLine(); } - SB.AppendLine(); - foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) { SB.AppendLine($"layout (std140) uniform {DeclInfo.Name} {{"); @@ -312,17 +311,27 @@ namespace Ryujinx.Graphics.Gal.Shader { if (Decl.ShaderType == GalShaderType.Fragment) { + int Count = 0; + for (int Attachment = 0; Attachment < 8; Attachment++) { if (Header.OmapTargets[Attachment].Enabled) { SB.AppendLine("layout (location = " + Attachment + ") out vec4 " + GlslDecl.FragmentOutputName + Attachment + ";"); + + Count++; } } + + if (Count > 0) + { + SB.AppendLine(); + } } else { SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") out vec4 " + GlslDecl.PositionOutAttrName + ";"); + SB.AppendLine(); } PrintDeclAttributes(Decl.OutAttributes.Values, "out"); @@ -360,7 +369,7 @@ namespace Ryujinx.Graphics.Gal.Shader private void PrintDeclSsy() { - SB.AppendLine("uint " + GlslDecl.SsyCursorName + ";"); + SB.AppendLine("uint " + GlslDecl.SsyCursorName + " = 0;"); SB.AppendLine("uint " + GlslDecl.SsyStackName + "[" + GlslDecl.SsyStackSize + "];" + Environment.NewLine); } @@ -558,6 +567,49 @@ namespace Ryujinx.Graphics.Gal.Shader } } + private void PrintNodes(ShaderIrBlock Block, ShaderIrNode[] Nodes) + { + foreach (ShaderIrNode Node in Nodes) + { + PrintNode(Block, Node, IdentationStr); + } + + if (Nodes.Length == 0) + { + SB.AppendLine(IdentationStr + "return 0u;"); + + return; + } + + ShaderIrNode Last = Nodes[Nodes.Length - 1]; + + bool UnconditionalFlowChange = false; + + if (Last is ShaderIrOp Op) + { + switch (Op.Inst) + { + case ShaderIrInst.Bra: + case ShaderIrInst.Exit: + case ShaderIrInst.Sync: + UnconditionalFlowChange = true; + break; + } + } + + if (!UnconditionalFlowChange) + { + if (Block.Next != null) + { + SB.AppendLine(IdentationStr + "return " + GetBlockPosition(Block.Next) + ";"); + } + else + { + SB.AppendLine(IdentationStr + "return 0u;"); + } + } + } + private void PrintNode(ShaderIrBlock Block, ShaderIrNode Node, string Identation) { if (Node is ShaderIrCond Cond) @@ -571,14 +623,7 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine(Identation + "if (" + IfExpr + ") {"); - if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra) - { - SB.AppendLine(Identation + IdentationStr + "return " + GetBlockPosition(Block.Branch) + ";"); - } - else - { - PrintNode(Block, Cond.Child, Identation + IdentationStr); - } + PrintNode(Block, Cond.Child, Identation + IdentationStr); SB.AppendLine(Identation + "}"); } @@ -641,6 +686,7 @@ namespace Ryujinx.Graphics.Gal.Shader default: SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); + break; } } @@ -654,39 +700,6 @@ namespace Ryujinx.Graphics.Gal.Shader } } - private void PrintNodes(ShaderIrBlock Block, ShaderIrNode[] Nodes) - { - foreach (ShaderIrNode Node in Nodes) - { - PrintNode(Block, Node, IdentationStr); - } - - if (Nodes.Length > 0) - { - ShaderIrNode Last = Nodes[Nodes.Length - 1]; - - bool UnconditionalFlowChange = false; - - if (Last is ShaderIrOp Op) - { - switch (Op.Inst) - { - case ShaderIrInst.Bra: - case ShaderIrInst.Exit: - case ShaderIrInst.Kil: - case ShaderIrInst.Sync: - UnconditionalFlowChange = true; - break; - } - } - - if (!UnconditionalFlowChange) - { - SB.AppendLine(IdentationStr + "return " + GetBlockPosition(Block.Next) + ";"); - } - } - } - private bool IsValidOutOper(ShaderIrNode Node) { if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index c2ee474b17..6957e30b2a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -6,32 +6,32 @@ namespace Ryujinx.Graphics.Gal.Shader { static partial class ShaderDecode { - public static void Bfe_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Bfe_C(ShaderIrBlock Block, long OpCode, int Position) { EmitBfe(Block, OpCode, ShaderOper.CR); } - public static void Bfe_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Bfe_I(ShaderIrBlock Block, long OpCode, int Position) { EmitBfe(Block, OpCode, ShaderOper.Imm); } - public static void Bfe_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Bfe_R(ShaderIrBlock Block, long OpCode, int Position) { EmitBfe(Block, OpCode, ShaderOper.RR); } - public static void Fadd_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Fadd_C(ShaderIrBlock Block, long OpCode, int Position) { EmitFadd(Block, OpCode, ShaderOper.CR); } - public static void Fadd_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Fadd_I(ShaderIrBlock Block, long OpCode, int Position) { EmitFadd(Block, OpCode, ShaderOper.Immf); } - public static void Fadd_I32(ShaderIrBlock Block, long OpCode, long Position) + public static void Fadd_I32(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode OperA = OpCode.Gpr8(); ShaderIrNode OperB = OpCode.Immf32_20(); @@ -49,47 +49,47 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); } - public static void Fadd_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Fadd_R(ShaderIrBlock Block, long OpCode, int Position) { EmitFadd(Block, OpCode, ShaderOper.RR); } - public static void Ffma_CR(ShaderIrBlock Block, long OpCode, long Position) + public static void Ffma_CR(ShaderIrBlock Block, long OpCode, int Position) { EmitFfma(Block, OpCode, ShaderOper.CR); } - public static void Ffma_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Ffma_I(ShaderIrBlock Block, long OpCode, int Position) { EmitFfma(Block, OpCode, ShaderOper.Immf); } - public static void Ffma_RC(ShaderIrBlock Block, long OpCode, long Position) + public static void Ffma_RC(ShaderIrBlock Block, long OpCode, int Position) { EmitFfma(Block, OpCode, ShaderOper.RC); } - public static void Ffma_RR(ShaderIrBlock Block, long OpCode, long Position) + public static void Ffma_RR(ShaderIrBlock Block, long OpCode, int Position) { EmitFfma(Block, OpCode, ShaderOper.RR); } - public static void Fmnmx_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Fmnmx_C(ShaderIrBlock Block, long OpCode, int Position) { EmitFmnmx(Block, OpCode, ShaderOper.CR); } - public static void Fmnmx_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Fmnmx_I(ShaderIrBlock Block, long OpCode, int Position) { EmitFmnmx(Block, OpCode, ShaderOper.Immf); } - public static void Fmnmx_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Fmnmx_R(ShaderIrBlock Block, long OpCode, int Position) { EmitFmnmx(Block, OpCode, ShaderOper.RR); } - public static void Fmul_I32(ShaderIrBlock Block, long OpCode, long Position) + public static void Fmul_I32(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode OperA = OpCode.Gpr8(); ShaderIrNode OperB = OpCode.Immf32_20(); @@ -99,62 +99,62 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); } - public static void Fmul_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Fmul_C(ShaderIrBlock Block, long OpCode, int Position) { EmitFmul(Block, OpCode, ShaderOper.CR); } - public static void Fmul_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Fmul_I(ShaderIrBlock Block, long OpCode, int Position) { EmitFmul(Block, OpCode, ShaderOper.Immf); } - public static void Fmul_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Fmul_R(ShaderIrBlock Block, long OpCode, int Position) { EmitFmul(Block, OpCode, ShaderOper.RR); } - public static void Fset_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Fset_C(ShaderIrBlock Block, long OpCode, int Position) { EmitFset(Block, OpCode, ShaderOper.CR); } - public static void Fset_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Fset_I(ShaderIrBlock Block, long OpCode, int Position) { EmitFset(Block, OpCode, ShaderOper.Immf); } - public static void Fset_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Fset_R(ShaderIrBlock Block, long OpCode, int Position) { EmitFset(Block, OpCode, ShaderOper.RR); } - public static void Fsetp_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Fsetp_C(ShaderIrBlock Block, long OpCode, int Position) { EmitFsetp(Block, OpCode, ShaderOper.CR); } - public static void Fsetp_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Fsetp_I(ShaderIrBlock Block, long OpCode, int Position) { EmitFsetp(Block, OpCode, ShaderOper.Immf); } - public static void Fsetp_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Fsetp_R(ShaderIrBlock Block, long OpCode, int Position) { EmitFsetp(Block, OpCode, ShaderOper.RR); } - public static void Iadd_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Iadd_C(ShaderIrBlock Block, long OpCode, int Position) { EmitIadd(Block, OpCode, ShaderOper.CR); } - public static void Iadd_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Iadd_I(ShaderIrBlock Block, long OpCode, int Position) { EmitIadd(Block, OpCode, ShaderOper.Imm); } - public static void Iadd_I32(ShaderIrBlock Block, long OpCode, long Position) + public static void Iadd_I32(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode OperA = OpCode.Gpr8(); ShaderIrNode OperB = OpCode.Imm32_20(); @@ -168,42 +168,42 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); } - public static void Iadd_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Iadd_R(ShaderIrBlock Block, long OpCode, int Position) { EmitIadd(Block, OpCode, ShaderOper.RR); } - public static void Iadd3_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Iadd3_C(ShaderIrBlock Block, long OpCode, int Position) { EmitIadd3(Block, OpCode, ShaderOper.CR); } - public static void Iadd3_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Iadd3_I(ShaderIrBlock Block, long OpCode, int Position) { EmitIadd3(Block, OpCode, ShaderOper.Imm); } - public static void Iadd3_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Iadd3_R(ShaderIrBlock Block, long OpCode, int Position) { EmitIadd3(Block, OpCode, ShaderOper.RR); } - public static void Imnmx_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Imnmx_C(ShaderIrBlock Block, long OpCode, int Position) { EmitImnmx(Block, OpCode, ShaderOper.CR); } - public static void Imnmx_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Imnmx_I(ShaderIrBlock Block, long OpCode, int Position) { EmitImnmx(Block, OpCode, ShaderOper.Imm); } - public static void Imnmx_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Imnmx_R(ShaderIrBlock Block, long OpCode, int Position) { EmitImnmx(Block, OpCode, ShaderOper.RR); } - public static void Ipa(ShaderIrBlock Block, long OpCode, long Position) + public static void Ipa(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode OperA = OpCode.Abuf28(); ShaderIrNode OperB = OpCode.Gpr20(); @@ -217,52 +217,52 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); } - public static void Iscadd_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Iscadd_C(ShaderIrBlock Block, long OpCode, int Position) { EmitIscadd(Block, OpCode, ShaderOper.CR); } - public static void Iscadd_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Iscadd_I(ShaderIrBlock Block, long OpCode, int Position) { EmitIscadd(Block, OpCode, ShaderOper.Imm); } - public static void Iscadd_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Iscadd_R(ShaderIrBlock Block, long OpCode, int Position) { EmitIscadd(Block, OpCode, ShaderOper.RR); } - public static void Iset_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Iset_C(ShaderIrBlock Block, long OpCode, int Position) { EmitIset(Block, OpCode, ShaderOper.CR); } - public static void Iset_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Iset_I(ShaderIrBlock Block, long OpCode, int Position) { EmitIset(Block, OpCode, ShaderOper.Imm); } - public static void Iset_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Iset_R(ShaderIrBlock Block, long OpCode, int Position) { EmitIset(Block, OpCode, ShaderOper.RR); } - public static void Isetp_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Isetp_C(ShaderIrBlock Block, long OpCode, int Position) { EmitIsetp(Block, OpCode, ShaderOper.CR); } - public static void Isetp_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Isetp_I(ShaderIrBlock Block, long OpCode, int Position) { EmitIsetp(Block, OpCode, ShaderOper.Imm); } - public static void Isetp_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Isetp_R(ShaderIrBlock Block, long OpCode, int Position) { EmitIsetp(Block, OpCode, ShaderOper.RR); } - public static void Lop_I32(ShaderIrBlock Block, long OpCode, long Position) + public static void Lop_I32(ShaderIrBlock Block, long OpCode, int Position) { int SubOp = OpCode.Read(53, 3); @@ -296,22 +296,22 @@ namespace Ryujinx.Graphics.Gal.Shader } } - public static void Lop_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Lop_C(ShaderIrBlock Block, long OpCode, int Position) { EmitLop(Block, OpCode, ShaderOper.CR); } - public static void Lop_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Lop_I(ShaderIrBlock Block, long OpCode, int Position) { EmitLop(Block, OpCode, ShaderOper.Imm); } - public static void Lop_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Lop_R(ShaderIrBlock Block, long OpCode, int Position) { EmitLop(Block, OpCode, ShaderOper.RR); } - public static void Mufu(ShaderIrBlock Block, long OpCode, long Position) + public static void Mufu(ShaderIrBlock Block, long OpCode, int Position) { int SubOp = OpCode.Read(20, 0xf); @@ -340,7 +340,7 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); } - public static void Psetp(ShaderIrBlock Block, long OpCode, long Position) + public static void Psetp(ShaderIrBlock Block, long OpCode, int Position) { bool NegA = OpCode.Read(15); bool NegB = OpCode.Read(32); @@ -394,47 +394,47 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); } - public static void Rro_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Rro_C(ShaderIrBlock Block, long OpCode, int Position) { EmitRro(Block, OpCode, ShaderOper.CR); } - public static void Rro_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Rro_I(ShaderIrBlock Block, long OpCode, int Position) { EmitRro(Block, OpCode, ShaderOper.Immf); } - public static void Rro_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Rro_R(ShaderIrBlock Block, long OpCode, int Position) { EmitRro(Block, OpCode, ShaderOper.RR); } - public static void Shl_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Shl_C(ShaderIrBlock Block, long OpCode, int Position) { EmitAluBinary(Block, OpCode, ShaderOper.CR, ShaderIrInst.Lsl); } - public static void Shl_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Shl_I(ShaderIrBlock Block, long OpCode, int Position) { EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Lsl); } - public static void Shl_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Shl_R(ShaderIrBlock Block, long OpCode, int Position) { EmitAluBinary(Block, OpCode, ShaderOper.RR, ShaderIrInst.Lsl); } - public static void Shr_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Shr_C(ShaderIrBlock Block, long OpCode, int Position) { EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode)); } - public static void Shr_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Shr_I(ShaderIrBlock Block, long OpCode, int Position) { EmitAluBinary(Block, OpCode, ShaderOper.Imm, GetShrInst(OpCode)); } - public static void Shr_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Shr_R(ShaderIrBlock Block, long OpCode, int Position) { EmitAluBinary(Block, OpCode, ShaderOper.RR, GetShrInst(OpCode)); } @@ -446,7 +446,7 @@ namespace Ryujinx.Graphics.Gal.Shader return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr; } - public static void Vmad(ShaderIrBlock Block, long OpCode, long Position) + public static void Vmad(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode OperA = OpCode.Gpr8(); @@ -481,22 +481,22 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Final))); } - public static void Xmad_CR(ShaderIrBlock Block, long OpCode, long Position) + public static void Xmad_CR(ShaderIrBlock Block, long OpCode, int Position) { EmitXmad(Block, OpCode, ShaderOper.CR); } - public static void Xmad_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Xmad_I(ShaderIrBlock Block, long OpCode, int Position) { EmitXmad(Block, OpCode, ShaderOper.Imm); } - public static void Xmad_RC(ShaderIrBlock Block, long OpCode, long Position) + public static void Xmad_RC(ShaderIrBlock Block, long OpCode, int Position) { EmitXmad(Block, OpCode, ShaderOper.RC); } - public static void Xmad_RR(ShaderIrBlock Block, long OpCode, long Position) + public static void Xmad_RR(ShaderIrBlock Block, long OpCode, int Position) { EmitXmad(Block, OpCode, ShaderOper.RR); } @@ -585,6 +585,7 @@ namespace Ryujinx.Graphics.Gal.Shader bool AbsA = OpCode.Read(46); bool NegA = OpCode.Read(48); bool AbsB = OpCode.Read(49); + bool Sat = OpCode.Read(50); ShaderIrNode OperA = OpCode.Gpr8(), OperB; @@ -603,12 +604,13 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB); - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); } private static void EmitFmul(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegB = OpCode.Read(48); + bool Sat = OpCode.Read(50); ShaderIrNode OperA = OpCode.Gpr8(), OperB; @@ -625,13 +627,14 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB); - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); } private static void EmitFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegB = OpCode.Read(48); bool NegC = OpCode.Read(49); + bool Sat = OpCode.Read(50); ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC; @@ -658,7 +661,7 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC); - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); } private static void EmitIadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) @@ -727,7 +730,7 @@ namespace Ryujinx.Graphics.Gal.Shader } ShaderIrNode Src1 = GetAluIneg(ApplyHeight(OpCode.Gpr8(), Height1), Neg1); - ShaderIrNode Src2 = GetAluIneg(ApplyHeight(OperB, Height2), Neg2); + ShaderIrNode Src2 = GetAluIneg(ApplyHeight(OperB, Height2), Neg2); ShaderIrNode Src3 = GetAluIneg(ApplyHeight(OpCode.Gpr39(), Height3), Neg3); ShaderIrOp Sum = new ShaderIrOp(ShaderIrInst.Add, Src1, Src2); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs index dfd10e0029..bc2539bd7e 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs @@ -1,12 +1,10 @@ using System; -using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; - namespace Ryujinx.Graphics.Gal.Shader { static partial class ShaderDecode { - public static void Bra(ShaderIrBlock Block, long OpCode, long Position) + public static void Bra(ShaderIrBlock Block, long OpCode, int Position) { if ((OpCode & 0x20) != 0) { @@ -15,14 +13,12 @@ namespace Ryujinx.Graphics.Gal.Shader throw new NotImplementedException(); } - int Target = OpCode.Branch(); - - ShaderIrOperImm Imm = new ShaderIrOperImm(Target); + ShaderIrOperImm Imm = new ShaderIrOperImm(Position + OpCode.Branch()); Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm))); } - public static void Exit(ShaderIrBlock Block, long OpCode, long Position) + public static void Exit(ShaderIrBlock Block, long OpCode, int Position) { int CCode = (int)OpCode & 0x1f; @@ -31,15 +27,14 @@ namespace Ryujinx.Graphics.Gal.Shader { Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Exit))); } - } - public static void Kil(ShaderIrBlock Block, long OpCode, long Position) + public static void Kil(ShaderIrBlock Block, long OpCode, int Position) { Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Kil))); } - public static void Ssy(ShaderIrBlock Block, long OpCode, long Position) + public static void Ssy(ShaderIrBlock Block, long OpCode, int Position) { if ((OpCode & 0x20) != 0) { @@ -48,19 +43,14 @@ namespace Ryujinx.Graphics.Gal.Shader throw new NotImplementedException(); } - int Offset = OpCode.Branch(); - - int Target = (int)(Position + Offset); - - ShaderIrOperImm Imm = new ShaderIrOperImm(Target); + ShaderIrOperImm Imm = new ShaderIrOperImm(Position + OpCode.Branch()); Block.AddNode(new ShaderIrOp(ShaderIrInst.Ssy, Imm)); } - public static void Sync(ShaderIrBlock Block, long OpCode, long Position) + public static void Sync(ShaderIrBlock Block, long OpCode, int Position) { //TODO: Implement Sync condition codes - Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Sync))); } } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFunc.cs similarity index 83% rename from Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs rename to Ryujinx.Graphics/Gal/Shader/ShaderDecodeFunc.cs index 73625f65fe..73248aa0c7 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFunc.cs @@ -1,4 +1,4 @@ namespace Ryujinx.Graphics.Gal.Shader { - delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode, long Position); + delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode, int Position); } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index 010f06aa6c..d07bcd9171 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -1,9 +1,10 @@ -using System; - namespace Ryujinx.Graphics.Gal.Shader { static class ShaderDecodeHelper { + private static readonly ShaderIrOperImmf ImmfZero = new ShaderIrOperImmf(0); + private static readonly ShaderIrOperImmf ImmfOne = new ShaderIrOperImmf(1); + public static ShaderIrNode GetAluFabsFneg(ShaderIrNode Node, bool Abs, bool Neg) { return GetAluFneg(GetAluFabs(Node, Abs), Neg); @@ -19,6 +20,11 @@ namespace Ryujinx.Graphics.Gal.Shader return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node; } + public static ShaderIrNode GetAluFsat(ShaderIrNode Node, bool Sat) + { + return Sat ? new ShaderIrOp(ShaderIrInst.Fclamp, Node, ImmfZero, ImmfOne) : Node; + } + public static ShaderIrNode GetAluIabsIneg(ShaderIrNode Node, bool Abs, bool Neg) { return GetAluIneg(GetAluIabs(Node, Abs), Neg); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index 508a0205dd..cd65599503 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gal.Shader { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } }; - public static void Ld_A(ShaderIrBlock Block, long OpCode, long Position) + public static void Ld_A(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode[] Opers = OpCode.Abuf20(); @@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Gal.Shader } } - public static void Ld_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Ld_C(ShaderIrBlock Block, long OpCode, int Position) { int CbufPos = OpCode.Read(22, 0x3fff); int CbufIndex = OpCode.Read(36, 0x1f); @@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.Gal.Shader } } - public static void St_A(ShaderIrBlock Block, long OpCode, long Position) + public static void St_A(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode[] Opers = OpCode.Abuf20(); @@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Gal.Shader } } - public static void Texq(ShaderIrBlock Block, long OpCode, long Position) + public static void Texq(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrNode OperD = OpCode.Gpr0(); ShaderIrNode OperA = OpCode.Gpr8(); @@ -132,12 +132,12 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperA, Op1))); //Is this right? } - public static void Tex(ShaderIrBlock Block, long OpCode, long Position) + public static void Tex(ShaderIrBlock Block, long OpCode, int Position) { EmitTex(Block, OpCode, GprHandle: false); } - public static void Tex_B(ShaderIrBlock Block, long OpCode, long Position) + public static void Tex_B(ShaderIrBlock Block, long OpCode, int Position) { EmitTex(Block, OpCode, GprHandle: true); } @@ -202,12 +202,12 @@ namespace Ryujinx.Graphics.Gal.Shader } } - public static void Texs(ShaderIrBlock Block, long OpCode, long Position) + public static void Texs(ShaderIrBlock Block, long OpCode, int Position) { EmitTexs(Block, OpCode, ShaderIrInst.Texs); } - public static void Tlds(ShaderIrBlock Block, long OpCode, long Position) + public static void Tlds(ShaderIrBlock Block, long OpCode, int Position) { EmitTexs(Block, OpCode, ShaderIrInst.Txlf); } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs index add3940273..cd602db7c1 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs @@ -25,67 +25,67 @@ namespace Ryujinx.Graphics.Gal.Shader F64 = 3 } - public static void F2f_C(ShaderIrBlock Block, long OpCode, long Position) + public static void F2f_C(ShaderIrBlock Block, long OpCode, int Position) { EmitF2f(Block, OpCode, ShaderOper.CR); } - public static void F2f_I(ShaderIrBlock Block, long OpCode, long Position) + public static void F2f_I(ShaderIrBlock Block, long OpCode, int Position) { EmitF2f(Block, OpCode, ShaderOper.Immf); } - public static void F2f_R(ShaderIrBlock Block, long OpCode, long Position) + public static void F2f_R(ShaderIrBlock Block, long OpCode, int Position) { EmitF2f(Block, OpCode, ShaderOper.RR); } - public static void F2i_C(ShaderIrBlock Block, long OpCode, long Position) + public static void F2i_C(ShaderIrBlock Block, long OpCode, int Position) { EmitF2i(Block, OpCode, ShaderOper.CR); } - public static void F2i_I(ShaderIrBlock Block, long OpCode, long Position) + public static void F2i_I(ShaderIrBlock Block, long OpCode, int Position) { EmitF2i(Block, OpCode, ShaderOper.Immf); } - public static void F2i_R(ShaderIrBlock Block, long OpCode, long Position) + public static void F2i_R(ShaderIrBlock Block, long OpCode, int Position) { EmitF2i(Block, OpCode, ShaderOper.RR); } - public static void I2f_C(ShaderIrBlock Block, long OpCode, long Position) + public static void I2f_C(ShaderIrBlock Block, long OpCode, int Position) { EmitI2f(Block, OpCode, ShaderOper.CR); } - public static void I2f_I(ShaderIrBlock Block, long OpCode, long Position) + public static void I2f_I(ShaderIrBlock Block, long OpCode, int Position) { EmitI2f(Block, OpCode, ShaderOper.Imm); } - public static void I2f_R(ShaderIrBlock Block, long OpCode, long Position) + public static void I2f_R(ShaderIrBlock Block, long OpCode, int Position) { EmitI2f(Block, OpCode, ShaderOper.RR); } - public static void I2i_C(ShaderIrBlock Block, long OpCode, long Position) + public static void I2i_C(ShaderIrBlock Block, long OpCode, int Position) { EmitI2i(Block, OpCode, ShaderOper.CR); } - public static void I2i_I(ShaderIrBlock Block, long OpCode, long Position) + public static void I2i_I(ShaderIrBlock Block, long OpCode, int Position) { EmitI2i(Block, OpCode, ShaderOper.Imm); } - public static void I2i_R(ShaderIrBlock Block, long OpCode, long Position) + public static void I2i_R(ShaderIrBlock Block, long OpCode, int Position) { EmitI2i(Block, OpCode, ShaderOper.RR); } - public static void Isberd(ShaderIrBlock Block, long OpCode, long Position) + public static void Isberd(ShaderIrBlock Block, long OpCode, int Position) { //This instruction seems to be used to translate from an address to a vertex index in a GS //Stub it as such @@ -95,50 +95,50 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OpCode.Gpr8()))); } - public static void Mov_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Mov_C(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrOperCbuf Cbuf = OpCode.Cbuf34(); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Cbuf))); } - public static void Mov_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Mov_I(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrOperImm Imm = OpCode.Imm19_20(); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Imm))); } - public static void Mov_I32(ShaderIrBlock Block, long OpCode, long Position) + public static void Mov_I32(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrOperImm Imm = OpCode.Imm32_20(); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Imm))); } - public static void Mov_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Mov_R(ShaderIrBlock Block, long OpCode, int Position) { ShaderIrOperGpr Gpr = OpCode.Gpr20(); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Gpr))); } - public static void Sel_C(ShaderIrBlock Block, long OpCode, long Position) + public static void Sel_C(ShaderIrBlock Block, long OpCode, int Position) { EmitSel(Block, OpCode, ShaderOper.CR); } - public static void Sel_I(ShaderIrBlock Block, long OpCode, long Position) + public static void Sel_I(ShaderIrBlock Block, long OpCode, int Position) { EmitSel(Block, OpCode, ShaderOper.Imm); } - public static void Sel_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Sel_R(ShaderIrBlock Block, long OpCode, int Position) { EmitSel(Block, OpCode, ShaderOper.RR); } - public static void Mov_S(ShaderIrBlock Block, long OpCode, long Position) + public static void Mov_S(ShaderIrBlock Block, long OpCode, int Position) { Block.AddNode(new ShaderIrCmnt("Stubbed.")); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs index c3e42654a9..35abdb7641 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs @@ -1,10 +1,8 @@ -using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; - -namespace Ryujinx.Graphics.Gal.Shader +namespace Ryujinx.Graphics.Gal.Shader { static partial class ShaderDecode { - public static void Out_R(ShaderIrBlock Block, long OpCode, long Position) + public static void Out_R(ShaderIrBlock Block, long OpCode, int Position) { //TODO: Those registers have to be used for something ShaderIrOperGpr Gpr0 = OpCode.Gpr0(); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index 81d8f31268..f8c07f31ab 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -10,12 +10,14 @@ namespace Ryujinx.Graphics.Gal.Shader public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start) { - Dictionary Visited = new Dictionary(); - Dictionary VisitedEnd = new Dictionary(); + Dictionary Visited = new Dictionary(); + Dictionary VisitedEnd = new Dictionary(); Queue Blocks = new Queue(); - ShaderIrBlock Enqueue(long Position, ShaderIrBlock Source = null) + long Beginning = Start + HeaderSize; + + ShaderIrBlock Enqueue(int Position, ShaderIrBlock Source = null) { if (!Visited.TryGetValue(Position, out ShaderIrBlock Output)) { @@ -34,13 +36,13 @@ namespace Ryujinx.Graphics.Gal.Shader return Output; } - ShaderIrBlock Entry = Enqueue(Start + HeaderSize); + ShaderIrBlock Entry = Enqueue(0); while (Blocks.Count > 0) { ShaderIrBlock Current = Blocks.Dequeue(); - FillBlock(Memory, Current, Start + HeaderSize); + FillBlock(Memory, Current, Beginning); //Set child blocks. "Branch" is the block the branch instruction //points to (when taken), "Next" is the block at the next address, @@ -54,22 +56,20 @@ namespace Ryujinx.Graphics.Gal.Shader if (InnerOp?.Inst == ShaderIrInst.Bra) { - int Offset = ((ShaderIrOperImm)InnerOp.OperandA).Value; - - long Target = Current.EndPosition + Offset; + int Target = ((ShaderIrOperImm)InnerOp.OperandA).Value; Current.Branch = Enqueue(Target, Current); } foreach (ShaderIrNode Node in Current.Nodes) { - if (Node is ShaderIrOp CurrOp && CurrOp.Inst == ShaderIrInst.Ssy) + InnerOp = GetInnermostOp(Node); + + if (InnerOp is ShaderIrOp CurrOp && CurrOp.Inst == ShaderIrInst.Ssy) { - int Offset = ((ShaderIrOperImm)CurrOp.OperandA).Value; + int Target = ((ShaderIrOperImm)CurrOp.OperandA).Value; - long Target = Offset; - - Current.Branch = Enqueue(Target, Current); + Enqueue(Target, Current); } } @@ -112,15 +112,15 @@ namespace Ryujinx.Graphics.Gal.Shader while (Visited.Count > 0) { - ulong FirstPos = ulong.MaxValue; + uint FirstPos = uint.MaxValue; foreach (ShaderIrBlock Block in Visited.Values) { - if (FirstPos > (ulong)Block.Position) - FirstPos = (ulong)Block.Position; + if (FirstPos > (uint)Block.Position) + FirstPos = (uint)Block.Position; } - ShaderIrBlock Current = Visited[(long)FirstPos]; + ShaderIrBlock Current = Visited[(int)FirstPos]; do { @@ -138,20 +138,20 @@ namespace Ryujinx.Graphics.Gal.Shader private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block, long Beginning) { - long Position = Block.Position; + int Position = Block.Position; do { //Ignore scheduling instructions, which are written every 32 bytes. - if (((Position - Beginning) & 0x1f) == 0) + if ((Position & 0x1f) == 0) { Position += 8; continue; } - uint Word0 = (uint)Memory.ReadInt32(Position + 0); - uint Word1 = (uint)Memory.ReadInt32(Position + 4); + uint Word0 = (uint)Memory.ReadInt32(Position + Beginning + 0); + uint Word1 = (uint)Memory.ReadInt32(Position + Beginning + 4); Position += 8; @@ -161,15 +161,15 @@ namespace Ryujinx.Graphics.Gal.Shader if (AddDbgComments) { - string DbgOpCode = $"0x{(Position - Beginning - 8):x16}: 0x{OpCode:x16} "; + string DbgOpCode = $"0x{(Position - 8):x16}: 0x{OpCode:x16} "; DbgOpCode += (Decode?.Method.Name ?? "???"); - if (Decode == ShaderDecode.Bra) + if (Decode == ShaderDecode.Bra || Decode == ShaderDecode.Ssy) { int Offset = ((int)(OpCode >> 20) << 8) >> 8; - long Target = Position + Offset - Beginning; + long Target = Position + Offset; DbgOpCode += " (0x" + Target.ToString("x16") + ")"; } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs index 50e563b846..782f96261b 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs @@ -4,8 +4,8 @@ namespace Ryujinx.Graphics.Gal.Shader { class ShaderIrBlock { - public long Position { get; set; } - public long EndPosition { get; set; } + public int Position { get; set; } + public int EndPosition { get; set; } public ShaderIrBlock Next { get; set; } public ShaderIrBlock Branch { get; set; } @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gal.Shader public List Nodes { get; private set; } - public ShaderIrBlock(long Position) + public ShaderIrBlock(int Position) { this.Position = Position; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 1e76eab169..d44659c755 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110000101x", ShaderDecode.Shr_C); Set("0011100x00101x", ShaderDecode.Shr_I); Set("0101110000101x", ShaderDecode.Shr_R); - Set("1110001010010x", ShaderDecode.Ssy); + Set("111000101001xx", ShaderDecode.Ssy); Set("1110111111110x", ShaderDecode.St_A); Set("1111000011111x", ShaderDecode.Sync); Set("110000xxxx111x", ShaderDecode.Tex); diff --git a/Ryujinx.Graphics/Gal/ShaderDumper.cs b/Ryujinx.Graphics/Gal/ShaderDumper.cs index 541368e89b..d3bcbf0d87 100644 --- a/Ryujinx.Graphics/Gal/ShaderDumper.cs +++ b/Ryujinx.Graphics/Gal/ShaderDumper.cs @@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Gal { private static string RuntimeDir; - private static int DumpIndex = 1; + public static int DumpIndex { get; private set; } = 1; public static void Dump(IGalMemory Memory, long Position, GalShaderType Type, string ExtSuffix = "") { - if (string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath)) + if (!IsDumpEnabled()) { return; } @@ -25,9 +25,10 @@ namespace Ryujinx.Graphics.Gal using (FileStream FullFile = File.Create(FullPath)) using (FileStream CodeFile = File.Create(CodePath)) - using (BinaryWriter FullWriter = new BinaryWriter(FullFile)) - using (BinaryWriter CodeWriter = new BinaryWriter(CodeFile)) { + BinaryWriter FullWriter = new BinaryWriter(FullFile); + BinaryWriter CodeWriter = new BinaryWriter(CodeFile); + for (long i = 0; i < 0x50; i += 4) { FullWriter.Write(Memory.ReadInt32(Position + i)); @@ -69,6 +70,11 @@ namespace Ryujinx.Graphics.Gal } } + public static bool IsDumpEnabled() + { + return !string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath); + } + private static string FullDir() { return CreateAndReturn(Path.Combine(DumpDir(), "Full")); diff --git a/Ryujinx.Graphics/GpuMethodCall.cs b/Ryujinx.Graphics/GpuMethodCall.cs new file mode 100644 index 0000000000..762d10f1d0 --- /dev/null +++ b/Ryujinx.Graphics/GpuMethodCall.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics +{ + struct GpuMethodCall + { + public int Method { get; private set; } + public int Argument { get; private set; } + public int SubChannel { get; private set; } + public int MethodCount { get; private set; } + + public bool IsLastCall => MethodCount <= 1; + + public GpuMethodCall( + int Method, + int Argument, + int SubChannel = 0, + int MethodCount = 0) + { + this.Method = Method; + this.Argument = Argument; + this.SubChannel = SubChannel; + this.MethodCount = MethodCount; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/GpuResourceManager.cs b/Ryujinx.Graphics/GpuResourceManager.cs new file mode 100644 index 0000000000..c3d697c5f9 --- /dev/null +++ b/Ryujinx.Graphics/GpuResourceManager.cs @@ -0,0 +1,145 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Memory; +using Ryujinx.Graphics.Texture; +using System.Collections.Generic; + +namespace Ryujinx.Graphics +{ + public class GpuResourceManager + { + private enum ImageType + { + None, + Texture, + ColorBuffer, + ZetaBuffer + } + + private NvGpu Gpu; + + private HashSet[] UploadedKeys; + + private Dictionary ImageTypes; + + public GpuResourceManager(NvGpu Gpu) + { + this.Gpu = Gpu; + + UploadedKeys = new HashSet[(int)NvGpuBufferType.Count]; + + for (int Index = 0; Index < UploadedKeys.Length; Index++) + { + UploadedKeys[Index] = new HashSet(); + } + + ImageTypes = new Dictionary(); + } + + public void SendColorBuffer(NvGpuVmm Vmm, long Position, int Attachment, GalImage NewImage) + { + long Size = (uint)ImageUtils.GetSize(NewImage); + + ImageTypes[Position] = ImageType.ColorBuffer; + + if (!TryReuse(Vmm, Position, NewImage)) + { + Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); + } + + Gpu.Renderer.RenderTarget.BindColor(Position, Attachment); + } + + public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage) + { + long Size = (uint)ImageUtils.GetSize(NewImage); + + ImageTypes[Position] = ImageType.ZetaBuffer; + + if (!TryReuse(Vmm, Position, NewImage)) + { + Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); + } + + Gpu.Renderer.RenderTarget.BindZeta(Position); + } + + public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage, int TexIndex = -1) + { + PrepareSendTexture(Vmm, Position, NewImage); + + if (TexIndex >= 0) + { + Gpu.Renderer.Texture.Bind(Position, TexIndex, NewImage); + } + + ImageTypes[Position] = ImageType.Texture; + } + + private void PrepareSendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage) + { + long Size = ImageUtils.GetSize(NewImage); + + bool SkipCheck = false; + + if (ImageTypes.TryGetValue(Position, out ImageType OldType)) + { + if (OldType == ImageType.ColorBuffer || OldType == ImageType.ZetaBuffer) + { + //Avoid data destruction + MemoryRegionModified(Vmm, Position, Size, NvGpuBufferType.Texture); + + SkipCheck = true; + } + } + + if (SkipCheck || !MemoryRegionModified(Vmm, Position, Size, NvGpuBufferType.Texture)) + { + if (TryReuse(Vmm, Position, NewImage)) + { + return; + } + } + + byte[] Data = ImageUtils.ReadTexture(Vmm, NewImage, Position); + + Gpu.Renderer.Texture.Create(Position, Data, NewImage); + } + + private bool TryReuse(NvGpuVmm Vmm, long Position, GalImage NewImage) + { + if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.SizeMatches(NewImage)) + { + Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage); + + return true; + } + + return false; + } + + public bool MemoryRegionModified(NvGpuVmm Vmm, long Position, long Size, NvGpuBufferType Type) + { + HashSet Uploaded = UploadedKeys[(int)Type]; + + if (!Uploaded.Add(Position)) + { + return false; + } + + return Vmm.IsRegionModified(Position, Size, Type); + } + + public void ClearPbCache() + { + for (int Index = 0; Index < UploadedKeys.Length; Index++) + { + UploadedKeys[Index].Clear(); + } + } + + public void ClearPbCache(NvGpuBufferType Type) + { + UploadedKeys[(int)Type].Clear(); + } + } +} diff --git a/Ryujinx.Graphics/INvGpuEngine.cs b/Ryujinx.Graphics/INvGpuEngine.cs index 810303b9f2..3e79efd3d5 100644 --- a/Ryujinx.Graphics/INvGpuEngine.cs +++ b/Ryujinx.Graphics/INvGpuEngine.cs @@ -6,6 +6,6 @@ namespace Ryujinx.Graphics { int[] Registers { get; } - void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry); + void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/MacroInterpreter.cs b/Ryujinx.Graphics/MacroInterpreter.cs index 20e7895b4a..86831bae18 100644 --- a/Ryujinx.Graphics/MacroInterpreter.cs +++ b/Ryujinx.Graphics/MacroInterpreter.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; using System; using System.Collections.Generic; @@ -388,14 +389,11 @@ namespace Ryujinx.Graphics { int Value; - //If we don't have any parameters in the FIFO, - //keep running the PFIFO engine until it writes the parameters. - while (!Fifo.TryDequeue(out Value)) + if (!Fifo.TryDequeue(out Value)) { - if (!PFifo.Step()) - { - return 0; - } + Logger.PrintWarning(LogClass.Gpu, "Macro attempted to fetch an inexistent argument."); + + return 0; } return Value; @@ -408,9 +406,9 @@ namespace Ryujinx.Graphics private void Send(NvGpuVmm Vmm, int Value) { - NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value); + GpuMethodCall MethCall = new GpuMethodCall(MethAddr, Value); - Engine.CallMethod(Vmm, PBEntry); + Engine.CallMethod(Vmm, MethCall); MethAddr += MethIncr; } diff --git a/Ryujinx.Graphics/Memory/NvGpuPBEntry.cs b/Ryujinx.Graphics/Memory/NvGpuPBEntry.cs deleted file mode 100644 index 6b93c16995..0000000000 --- a/Ryujinx.Graphics/Memory/NvGpuPBEntry.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.ObjectModel; - -namespace Ryujinx.Graphics.Memory -{ - public struct NvGpuPBEntry - { - public int Method { get; private set; } - - public int SubChannel { get; private set; } - - private int[] m_Arguments; - - public ReadOnlyCollection Arguments => Array.AsReadOnly(m_Arguments); - - public NvGpuPBEntry(int Method, int SubChannel, params int[] Arguments) - { - this.Method = Method; - this.SubChannel = SubChannel; - this.m_Arguments = Arguments; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Memory/NvGpuPushBuffer.cs b/Ryujinx.Graphics/Memory/NvGpuPushBuffer.cs deleted file mode 100644 index 0902ebfc92..0000000000 --- a/Ryujinx.Graphics/Memory/NvGpuPushBuffer.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace Ryujinx.Graphics.Memory -{ - public static class NvGpuPushBuffer - { - private enum SubmissionMode - { - Incrementing = 1, - NonIncrementing = 3, - Immediate = 4, - IncrementOnce = 5 - } - - public static NvGpuPBEntry[] Decode(byte[] Data) - { - using (MemoryStream MS = new MemoryStream(Data)) - { - BinaryReader Reader = new BinaryReader(MS); - - List PushBuffer = new List(); - - bool CanRead() => MS.Position + 4 <= MS.Length; - - while (CanRead()) - { - int Packed = Reader.ReadInt32(); - - int Meth = (Packed >> 0) & 0x1fff; - int SubC = (Packed >> 13) & 7; - int Args = (Packed >> 16) & 0x1fff; - int Mode = (Packed >> 29) & 7; - - switch ((SubmissionMode)Mode) - { - case SubmissionMode.Incrementing: - { - for (int Index = 0; Index < Args && CanRead(); Index++, Meth++) - { - PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32())); - } - - break; - } - - case SubmissionMode.NonIncrementing: - { - int[] Arguments = new int[Args]; - - for (int Index = 0; Index < Arguments.Length; Index++) - { - if (!CanRead()) - { - break; - } - - Arguments[Index] = Reader.ReadInt32(); - } - - PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Arguments)); - - break; - } - - case SubmissionMode.Immediate: - { - PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Args)); - - break; - } - - case SubmissionMode.IncrementOnce: - { - if (CanRead()) - { - PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32())); - } - - if (CanRead() && Args > 1) - { - int[] Arguments = new int[Args - 1]; - - for (int Index = 0; Index < Arguments.Length && CanRead(); Index++) - { - Arguments[Index] = Reader.ReadInt32(); - } - - PushBuffer.Add(new NvGpuPBEntry(Meth + 1, SubC, Arguments)); - } - - break; - } - } - } - - return PushBuffer.ToArray(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Memory/NvGpuVmm.cs b/Ryujinx.Graphics/Memory/NvGpuVmm.cs index 0082fb2751..ec660bf0e9 100644 --- a/Ryujinx.Graphics/Memory/NvGpuVmm.cs +++ b/Ryujinx.Graphics/Memory/NvGpuVmm.cs @@ -4,7 +4,7 @@ using System; namespace Ryujinx.Graphics.Memory { - public class NvGpuVmm : IAMemory, IGalMemory + public class NvGpuVmm : IMemory, IGalMemory { public const long AddrSize = 1L << 40; @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Memory private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; private const int PTLvl1Bit = PTPageBits; - public AMemory Memory { get; private set; } + public MemoryManager Memory { get; private set; } private NvGpuVmmCache Cache; @@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Memory private long[][] PageTable; - public NvGpuVmm(AMemory Memory) + public NvGpuVmm(MemoryManager Memory) { this.Memory = Memory; diff --git a/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs index 56979e1f06..dd6d37c9f1 100644 --- a/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs +++ b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs @@ -1,309 +1,130 @@ using ChocolArm64.Memory; using System; -using System.Collections.Generic; namespace Ryujinx.Graphics.Memory { class NvGpuVmmCache { - private const long RamSize = 4L * 1024 * 1024 * 1024; - - private const int MaxCpCount = 10000; - private const int MaxCpTimeDelta = 60000; - - private class CachedPage + private struct CachedResource { - private struct Range - { - public long Start; - public long End; + public long Key; + public int Mask; - public Range(long Start, long End) - { - this.Start = Start; - this.End = End; - } + public CachedResource(long Key, int Mask) + { + this.Key = Key; + this.Mask = Mask; } - private List[] Regions; - - private HashSet ResidencyKeys; - - public LinkedListNode Node { get; set; } - - public int Timestamp { get; private set; } - - public CachedPage() + public override int GetHashCode() { - Regions = new List[(int)NvGpuBufferType.Count]; - - for (int Index = 0; Index < Regions.Length; Index++) - { - Regions[Index] = new List(); - } - - ResidencyKeys = new HashSet(); + return (int)(Key * 23 + Mask); } - public void AddResidency(long Key) + public override bool Equals(object obj) { - ResidencyKeys.Add(Key); + return obj is CachedResource Cached && Equals(Cached); } - public void RemoveResidency(HashSet[] Residency, long PageSize) + public bool Equals(CachedResource other) { - for (int i = 0; i < (int)NvGpuBufferType.Count; i++) - { - foreach (Range Region in Regions[i]) - { - foreach (long Key in ResidencyKeys) - { - Residency[Region.Start / PageSize].Remove(Key); - } - } - } - } - - public bool AddRange(long Start, long End, NvGpuBufferType BufferType) - { - List BtRegions = Regions[(int)BufferType]; - - for (int Index = 0; Index < BtRegions.Count; Index++) - { - Range Rg = BtRegions[Index]; - - if (Start >= Rg.Start && End <= Rg.End) - { - return false; - } - - if (Start <= Rg.End && Rg.Start <= End) - { - long MinStart = Math.Min(Rg.Start, Start); - long MaxEnd = Math.Max(Rg.End, End); - - BtRegions[Index] = new Range(MinStart, MaxEnd); - - Timestamp = Environment.TickCount; - - return true; - } - } - - BtRegions.Add(new Range(Start, End)); - - Timestamp = Environment.TickCount; - - return true; - } - - public int GetTotalCount() - { - int Count = 0; - - for (int Index = 0; Index < Regions.Length; Index++) - { - Count += Regions[Index].Count; - } - - return Count; + return Key == other.Key && Mask == other.Mask; } } - private Dictionary Cache; - - private LinkedList SortedCache; - - private HashSet[] Residency; - - private long ResidencyPageSize; - - private int CpCount; + private ValueRangeSet CachedRanges; public NvGpuVmmCache() { - Cache = new Dictionary(); - - SortedCache = new LinkedList(); + CachedRanges = new ValueRangeSet(); } - public bool IsRegionModified(AMemory Memory, NvGpuBufferType BufferType, long PA, long Size) + public bool IsRegionModified(MemoryManager Memory, NvGpuBufferType BufferType, long Start, long Size) { - (bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size); - - PA = Memory.GetPhysicalAddress(PA); - - ClearCachedPagesIfNeeded(); - - long PageSize = AMemory.PageSize; - - EnsureResidencyInitialized(PageSize); - - bool HasResidents = AddResidency(PA, Size); - - if (!HasResidents && ModifiedCount == 0) - { - return false; - } - - long Mask = PageSize - 1; - - long ResidencyKey = PA; - - long PAEnd = PA + Size; - - bool RegMod = false; + (bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(Start, Size); + //Remove all modified ranges. int Index = 0; - while (PA < PAEnd) + long Position = Start & ~NvGpuVmm.PageMask; + + while (ModifiedCount > 0) { - long Key = PA & ~AMemory.PageMask; - - long PAPgEnd = Math.Min((PA + AMemory.PageSize) & ~AMemory.PageMask, PAEnd); - - bool IsCached = Cache.TryGetValue(Key, out CachedPage Cp); - - if (IsCached) + if (Modified[Index++]) { - CpCount -= Cp.GetTotalCount(); + CachedRanges.Remove(new ValueRange(Position, Position + NvGpuVmm.PageSize)); - SortedCache.Remove(Cp.Node); - } - else - { - Cp = new CachedPage(); - - Cache.Add(Key, Cp); + ModifiedCount--; } - if (Modified[Index++] && IsCached) - { - Cp = new CachedPage(); + Position += NvGpuVmm.PageSize; + } - Cache[Key] = Cp; + //Mask has the bit set for the current resource type. + //If the region is not yet present on the list, then a new ValueRange + //is directly added with the current resource type as the only bit set. + //Otherwise, it just sets the bit for this new resource type on the current mask. + //The physical address of the resource is used as key, those keys are used to keep + //track of resources that are already on the cache. A resource may be inside another + //resource, and in this case we should return true if the "sub-resource" was not + //yet cached. + int Mask = 1 << (int)BufferType; + + CachedResource NewCachedValue = new CachedResource(Start, Mask); + + ValueRange NewCached = new ValueRange(Start, Start + Size); + + ValueRange[] Ranges = CachedRanges.GetAllIntersections(NewCached); + + bool IsKeyCached = Ranges.Length > 0 && Ranges[0].Value.Key == Start; + + long LastEnd = NewCached.Start; + + long Coverage = 0; + + for (Index = 0; Index < Ranges.Length; Index++) + { + ValueRange Current = Ranges[Index]; + + CachedResource Cached = Current.Value; + + long RgStart = Math.Max(Current.Start, NewCached.Start); + long RgEnd = Math.Min(Current.End, NewCached.End); + + if ((Cached.Mask & Mask) != 0) + { + Coverage += RgEnd - RgStart; } - Cp.AddResidency(ResidencyKey); - - Cp.Node = SortedCache.AddLast(Key); - - RegMod |= Cp.AddRange(PA, PAPgEnd, BufferType); - - CpCount += Cp.GetTotalCount(); - - PA = PAPgEnd; - } - - return RegMod; - } - - private bool AddResidency(long PA, long Size) - { - long PageSize = ResidencyPageSize; - - long Mask = PageSize - 1; - - long Key = PA; - - bool ResidentFound = false; - - for (long Cursor = PA & ~Mask; Cursor < ((PA + Size + PageSize - 1) & ~Mask); Cursor += PageSize) - { - long PageIndex = Cursor / PageSize; - - Residency[PageIndex].Add(Key); - - if (Residency[PageIndex].Count > 1) + //Highest key value has priority, this prevents larger resources + //for completely invalidating smaller ones on the cache. For example, + //consider that a resource in the range [100, 200) was added, and then + //another one in the range [50, 200). We prevent the new resource from + //completely replacing the old one by spliting it like this: + //New resource key is added at [50, 100), old key is still present at [100, 200). + if (Cached.Key < Start) { - ResidentFound = true; - } - } - - return ResidentFound; - } - - private void EnsureResidencyInitialized(long PageSize) - { - if (Residency == null) - { - Residency = new HashSet[RamSize / PageSize]; - - for (int i = 0; i < Residency.Length; i++) - { - Residency[i] = new HashSet(); + Cached.Key = Start; } - ResidencyPageSize = PageSize; - } - else - { - if (ResidencyPageSize != PageSize) + Cached.Mask |= Mask; + + CachedRanges.Add(new ValueRange(RgStart, RgEnd, Cached)); + + if (RgStart > LastEnd) { - throw new InvalidOperationException("Tried to change residency page size"); - } - } - } - - private void ClearCachedPagesIfNeeded() - { - if (CpCount <= MaxCpCount) - { - return; - } - - int Timestamp = Environment.TickCount; - - int TimeDelta; - - do - { - if (!TryPopOldestCachedPageKey(Timestamp, out long Key)) - { - break; + CachedRanges.Add(new ValueRange(LastEnd, RgStart, NewCachedValue)); } - CachedPage Cp = Cache[Key]; - - Cp.RemoveResidency(Residency, ResidencyPageSize); - - Cache.Remove(Key); - - CpCount -= Cp.GetTotalCount(); - - TimeDelta = RingDelta(Cp.Timestamp, Timestamp); + LastEnd = RgEnd; } - while (CpCount > (MaxCpCount >> 1) || (uint)TimeDelta > (uint)MaxCpTimeDelta); - } - private bool TryPopOldestCachedPageKey(int Timestamp, out long Key) - { - LinkedListNode Node = SortedCache.First; - - if (Node == null) + if (LastEnd < NewCached.End) { - Key = 0; - - return false; + CachedRanges.Add(new ValueRange(LastEnd, NewCached.End, NewCachedValue)); } - SortedCache.Remove(Node); - - Key = Node.Value; - - return true; - } - - private int RingDelta(int Old, int New) - { - if ((uint)New < (uint)Old) - { - return New + (~Old + 1); - } - else - { - return New - Old; - } + return !IsKeyCached || Coverage != Size; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpu.cs b/Ryujinx.Graphics/NvGpu.cs index 7ba700b89e..6989be98a2 100644 --- a/Ryujinx.Graphics/NvGpu.cs +++ b/Ryujinx.Graphics/NvGpu.cs @@ -1,5 +1,4 @@ using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics; namespace Ryujinx.Graphics { @@ -7,21 +6,29 @@ namespace Ryujinx.Graphics { public IGalRenderer Renderer { get; private set; } - public NvGpuFifo Fifo { get; private set; } + public GpuResourceManager ResourceManager { get; private set; } - public NvGpuEngine2d Engine2d { get; private set; } - public NvGpuEngine3d Engine3d { get; private set; } - public NvGpuEngineDma EngineDma { get; private set; } + public DmaPusher Pusher { get; private set; } + + internal NvGpuFifo Fifo { get; private set; } + internal NvGpuEngine2d Engine2d { get; private set; } + internal NvGpuEngine3d Engine3d { get; private set; } + internal NvGpuEngineM2mf EngineM2mf { get; private set; } + internal NvGpuEngineP2mf EngineP2mf { get; private set; } public NvGpu(IGalRenderer Renderer) { this.Renderer = Renderer; - Fifo = new NvGpuFifo(this); + ResourceManager = new GpuResourceManager(this); - Engine2d = new NvGpuEngine2d(this); - Engine3d = new NvGpuEngine3d(this); - EngineDma = new NvGpuEngineDma(this); + Pusher = new DmaPusher(this); + + Fifo = new NvGpuFifo(this); + Engine2d = new NvGpuEngine2d(this); + Engine3d = new NvGpuEngine3d(this); + EngineM2mf = new NvGpuEngineM2mf(this); + EngineP2mf = new NvGpuEngineP2mf(this); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Memory/NvGpuBufferType.cs b/Ryujinx.Graphics/NvGpuBufferType.cs similarity index 79% rename from Ryujinx.Graphics/Memory/NvGpuBufferType.cs rename to Ryujinx.Graphics/NvGpuBufferType.cs index 6f0d257188..f6b555970d 100644 --- a/Ryujinx.Graphics/Memory/NvGpuBufferType.cs +++ b/Ryujinx.Graphics/NvGpuBufferType.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Memory +namespace Ryujinx.Graphics { public enum NvGpuBufferType { diff --git a/Ryujinx.Graphics/NvGpuEngine.cs b/Ryujinx.Graphics/NvGpuEngine.cs index 3d5e118a59..d533e47849 100644 --- a/Ryujinx.Graphics/NvGpuEngine.cs +++ b/Ryujinx.Graphics/NvGpuEngine.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics _2d = 0x902d, _3d = 0xb197, Compute = 0xb1c0, - Kepler = 0xa140, - Dma = 0xb0b5 + P2mf = 0xa140, + M2mf = 0xb0b5 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuEngine2d.cs b/Ryujinx.Graphics/NvGpuEngine2d.cs index f26b00204b..f20f8d6eeb 100644 --- a/Ryujinx.Graphics/NvGpuEngine2d.cs +++ b/Ryujinx.Graphics/NvGpuEngine2d.cs @@ -1,12 +1,10 @@ using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Texture; -using System; -using System.Collections.Generic; namespace Ryujinx.Graphics { - public class NvGpuEngine2d : INvGpuEngine + class NvGpuEngine2d : INvGpuEngine { private enum CopyOperation { @@ -23,64 +21,54 @@ namespace Ryujinx.Graphics private NvGpu Gpu; - private Dictionary Methods; - public NvGpuEngine2d(NvGpu Gpu) { this.Gpu = Gpu; - Registers = new int[0xe00]; - - Methods = new Dictionary(); - - void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) - { - while (Count-- > 0) - { - Methods.Add(Meth, Method); - - Meth += Stride; - } - } - - AddMethod(0xb5, 1, 1, TextureCopy); + Registers = new int[0x238]; } - public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) { - if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) + WriteRegister(MethCall); + + if ((NvGpuEngine2dReg)MethCall.Method == NvGpuEngine2dReg.BlitSrcYInt) { - Method(Vmm, PBEntry); - } - else - { - WriteRegister(PBEntry); + TextureCopy(Vmm); } } - private void TextureCopy(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + private void TextureCopy(NvGpuVmm Vmm) { CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); - bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; - int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); - int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); - int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); - int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); - + int DstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat); bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); - TextureSwizzle SrcSwizzle = SrcLinear - ? TextureSwizzle.Pitch - : TextureSwizzle.BlockLinear; + int SrcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat); + bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; + int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); + int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); + int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); + int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); - TextureSwizzle DstSwizzle = DstLinear - ? TextureSwizzle.Pitch - : TextureSwizzle.BlockLinear; + int DstBlitX = ReadRegister(NvGpuEngine2dReg.BlitDstX); + int DstBlitY = ReadRegister(NvGpuEngine2dReg.BlitDstY); + int DstBlitW = ReadRegister(NvGpuEngine2dReg.BlitDstW); + int DstBlitH = ReadRegister(NvGpuEngine2dReg.BlitDstH); + + int SrcBlitX = ReadRegister(NvGpuEngine2dReg.BlitSrcXInt); + int SrcBlitY = ReadRegister(NvGpuEngine2dReg.BlitSrcYInt); + + GalImageFormat SrcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)SrcFormat); + GalImageFormat DstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)DstFormat); + + GalMemoryLayout SrcLayout = GetLayout(SrcLinear); + GalMemoryLayout DstLayout = GetLayout(DstLinear); int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf); int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); @@ -91,91 +79,63 @@ namespace Ryujinx.Graphics long SrcKey = Vmm.GetPhysicalAddress(SrcAddress); long DstKey = Vmm.GetPhysicalAddress(DstAddress); - bool IsSrcFb = Gpu.Engine3d.IsFrameBufferPosition(SrcKey); - bool IsDstFb = Gpu.Engine3d.IsFrameBufferPosition(DstKey); + GalImage SrcTexture = new GalImage( + SrcWidth, + SrcHeight, 1, + SrcBlockHeight, + SrcLayout, + SrcImgFormat); - TextureInfo SrcTexture() - { - return new TextureInfo( - SrcAddress, - SrcWidth, - SrcHeight, - SrcPitch, - SrcBlockHeight, 1, - SrcSwizzle, - GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm); - } + GalImage DstTexture = new GalImage( + DstWidth, + DstHeight, 1, + DstBlockHeight, + DstLayout, + DstImgFormat); - TextureInfo DstTexture() - { - return new TextureInfo( - DstAddress, - DstWidth, - DstHeight, - DstPitch, - DstBlockHeight, 1, - DstSwizzle, - GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm); - } + SrcTexture.Pitch = SrcPitch; + DstTexture.Pitch = DstPitch; - //TODO: fb -> fb copies, tex -> fb copies, formats other than RGBA8, - //make it throw for unimpl stuff (like the copy mode)... - if (IsSrcFb && IsDstFb) - { - //Frame Buffer -> Frame Buffer copy. - Gpu.Renderer.RenderTarget.Copy( - SrcKey, - DstKey, - 0, - 0, - SrcWidth, - SrcHeight, - 0, - 0, - DstWidth, - DstHeight); - } - if (IsSrcFb) - { - //Frame Buffer -> Texture copy. - Gpu.Renderer.RenderTarget.GetBufferData(SrcKey, (byte[] Buffer) => - { - TextureInfo Src = SrcTexture(); - TextureInfo Dst = DstTexture(); + Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture); + Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture); - if (Src.Width != Dst.Width || - Src.Height != Dst.Height) - { - throw new NotImplementedException("Texture resizing is not supported"); - } + Gpu.Renderer.RenderTarget.Copy( + SrcKey, + DstKey, + SrcBlitX, + SrcBlitY, + SrcBlitX + DstBlitW, + SrcBlitY + DstBlitH, + DstBlitX, + DstBlitY, + DstBlitX + DstBlitW, + DstBlitY + DstBlitH); - TextureWriter.Write(Vmm, Dst, Buffer); - }); - } - else if (IsDstFb) - { - byte[] Buffer = TextureReader.Read(Vmm, SrcTexture()); + //Do a guest side copy aswell. This is necessary when + //the texture is modified by the guest, however it doesn't + //work when resources that the gpu can write to are copied, + //like framebuffers. + ImageUtils.CopyTexture( + Vmm, + SrcTexture, + DstTexture, + SrcAddress, + DstAddress, + SrcBlitX, + SrcBlitY, + DstBlitX, + DstBlitY, + DstBlitW, + DstBlitH); - Gpu.Renderer.RenderTarget.SetBufferData( - DstKey, - DstWidth, - DstHeight, - Buffer); - } - else - { - //Texture -> Texture copy. - TextureInfo Src = SrcTexture(); - TextureInfo Dst = DstTexture(); + Vmm.IsRegionModified(DstKey, ImageUtils.GetSize(DstTexture), NvGpuBufferType.Texture); + } - if (Src.Width != Dst.Width || - Src.Height != Dst.Height) - { - throw new NotImplementedException("Texture resizing is not supported"); - } - - TextureWriter.Write(Vmm, Dst, TextureReader.Read(Vmm, Src)); - } + private static GalMemoryLayout GetLayout(bool Linear) + { + return Linear + ? GalMemoryLayout.Pitch + : GalMemoryLayout.BlockLinear; } private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg) @@ -185,24 +145,14 @@ namespace Ryujinx.Graphics (uint)Registers[(int)Reg + 1]; } - private void WriteRegister(NvGpuPBEntry PBEntry) + private void WriteRegister(GpuMethodCall MethCall) { - int ArgsCount = PBEntry.Arguments.Count; - - if (ArgsCount > 0) - { - Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1]; - } + Registers[MethCall.Method] = MethCall.Argument; } private int ReadRegister(NvGpuEngine2dReg Reg) { return Registers[(int)Reg]; } - - private void WriteRegister(NvGpuEngine2dReg Reg, int Value) - { - Registers[(int)Reg] = Value; - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuEngine2dReg.cs b/Ryujinx.Graphics/NvGpuEngine2dReg.cs index 00f6f578db..fe00374583 100644 --- a/Ryujinx.Graphics/NvGpuEngine2dReg.cs +++ b/Ryujinx.Graphics/NvGpuEngine2dReg.cs @@ -20,6 +20,20 @@ namespace Ryujinx.Graphics SrcWidth = 0x92, SrcHeight = 0x93, SrcAddress = 0x94, - CopyOperation = 0xab + ClipEnable = 0xa4, + CopyOperation = 0xab, + BlitControl = 0x223, + BlitDstX = 0x22c, + BlitDstY = 0x22d, + BlitDstW = 0x22e, + BlitDstH = 0x22f, + BlitDuDxFract = 0x230, + BlitDuDxInt = 0x231, + BlitDvDyFract = 0x232, + BlitDvDyInt = 0x233, + BlitSrcXFract = 0x234, + BlitSrcXInt = 0x235, + BlitSrcYFract = 0x236, + BlitSrcYInt = 0x237 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs index 624eddae0a..4a0310fb3f 100644 --- a/Ryujinx.Graphics/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/NvGpuEngine3d.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Texture; @@ -6,7 +7,7 @@ using System.Collections.Generic; namespace Ryujinx.Graphics { - public class NvGpuEngine3d : INvGpuEngine + class NvGpuEngine3d : INvGpuEngine { public int[] Registers { get; private set; } @@ -23,10 +24,6 @@ namespace Ryujinx.Graphics private ConstBuffer[][] ConstBuffers; - private HashSet FrameBuffers; - - private List[] UploadedKeys; - private int CurrentInstance = 0; public NvGpuEngine3d(NvGpu Gpu) @@ -60,53 +57,53 @@ namespace Ryujinx.Graphics ConstBuffers[Index] = new ConstBuffer[18]; } - FrameBuffers = new HashSet(); + //Ensure that all components are enabled by default. + //FIXME: Is this correct? + WriteRegister(NvGpuEngine3dReg.ColorMaskN, 0x1111); - UploadedKeys = new List[(int)NvGpuBufferType.Count]; + WriteRegister(NvGpuEngine3dReg.FrameBufferSrgb, 1); - for (int i = 0; i < UploadedKeys.Length; i++) + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) { - UploadedKeys[i] = new List(); + WriteRegister(NvGpuEngine3dReg.IBlendNEquationRgb + Index * 8, (int)GalBlendEquation.FuncAdd); + WriteRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb + Index * 8, (int)GalBlendFactor.One); + WriteRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb + Index * 8, (int)GalBlendFactor.Zero); + WriteRegister(NvGpuEngine3dReg.IBlendNEquationAlpha + Index * 8, (int)GalBlendEquation.FuncAdd); + WriteRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha + Index * 8, (int)GalBlendFactor.One); + WriteRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha + Index * 8, (int)GalBlendFactor.Zero); } } - public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) { - if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) + if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method)) { - Method(Vmm, PBEntry); + Method(Vmm, MethCall); } else { - WriteRegister(PBEntry); + WriteRegister(MethCall); } } - public void ResetCache() - { - foreach (List Uploaded in UploadedKeys) - { - Uploaded.Clear(); - } - } - - private void VertexEndGl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + private void VertexEndGl(NvGpuVmm Vmm, GpuMethodCall MethCall) { LockCaches(); GalPipelineState State = new GalPipelineState(); - SetFlip(State); + SetFrameBuffer(State); SetFrontFace(State); SetCullFace(State); SetDepth(State); SetStencil(State); - SetAlphaBlending(State); + SetBlending(State); + SetColorMask(State); SetPrimitiveRestart(State); for (int FbIndex = 0; FbIndex < 8; FbIndex++) { - SetFrameBuffer(Vmm, 0); + SetFrameBuffer(Vmm, FbIndex); } SetZeta(Vmm); @@ -140,13 +137,11 @@ namespace Ryujinx.Graphics Gpu.Renderer.Texture.UnlockCache(); } - private void ClearBuffers(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + private void ClearBuffers(NvGpuVmm Vmm, GpuMethodCall MethCall) { - int Arg0 = PBEntry.Arguments[0]; + int Attachment = (MethCall.Argument >> 6) & 0xf; - int FbIndex = (Arg0 >> 6) & 0xf; - - GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); + GalClearBufferFlags Flags = (GalClearBufferFlags)(MethCall.Argument & 0x3f); float Red = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 0); float Green = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 1); @@ -157,25 +152,27 @@ namespace Ryujinx.Graphics int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); - SetFrameBuffer(Vmm, FbIndex); + SetFrameBuffer(Vmm, Attachment); SetZeta(Vmm); - Gpu.Renderer.Rasterizer.ClearBuffers( - Flags, - FbIndex, - Red, Green, Blue, Alpha, - Depth, - Stencil); + SetRenderTargets(); + + Gpu.Renderer.RenderTarget.Bind(); + + Gpu.Renderer.Rasterizer.ClearBuffers(Flags, Attachment, Red, Green, Blue, Alpha, Depth, Stencil); + + Gpu.Renderer.Pipeline.ResetDepthMask(); + Gpu.Renderer.Pipeline.ResetColorMask(Attachment); } private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) { long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); - int Format = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10); + int SurfFormat = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10); - if (VA == 0 || Format == 0) + if (VA == 0 || SurfFormat == 0) { Gpu.Renderer.RenderTarget.UnbindColor(FbIndex); @@ -184,11 +181,15 @@ namespace Ryujinx.Graphics long Key = Vmm.GetPhysicalAddress(VA); - FrameBuffers.Add(Key); - int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); + int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10); + + int GobBlockHeight = 1 << ((BlockDim >> 4) & 7); + + GalMemoryLayout Layout = (GalMemoryLayout)((BlockDim >> 12) & 1); + float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 8); float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 8); @@ -201,48 +202,63 @@ namespace Ryujinx.Graphics int VpW = (int)(TX + MathF.Abs(SX)) - VpX; int VpH = (int)(TY + MathF.Abs(SY)) - VpY; - GalImageFormat ImageFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)Format); + GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat); - GalImage Image = new GalImage(Width, Height, ImageFormat); + GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); - long Size = ImageUtils.GetSize(Image); + Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); - Gpu.Renderer.Texture.CreateFb(Key, Size, Image); + Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH); + } - Gpu.Renderer.RenderTarget.BindColor(Key, FbIndex); + private void SetFrameBuffer(GalPipelineState State) + { + State.FramebufferSrgb = ReadRegisterBool(NvGpuEngine3dReg.FrameBufferSrgb); - Gpu.Renderer.RenderTarget.SetViewport(VpX, VpY, VpW, VpH); + State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); + State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); + + int ScreenYControl = ReadRegister(NvGpuEngine3dReg.ScreenYControl); + + bool NegateY = (ScreenYControl & 1) != 0; + + if (NegateY) + { + State.FlipY = -State.FlipY; + } } private void SetZeta(NvGpuVmm Vmm) { - long ZA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); + long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); - int Format = ReadRegister(NvGpuEngine3dReg.ZetaFormat); + int ZetaFormat = ReadRegister(NvGpuEngine3dReg.ZetaFormat); - bool ZetaEnable = (ReadRegister(NvGpuEngine3dReg.ZetaEnable) & 1) != 0; + int BlockDim = ReadRegister(NvGpuEngine3dReg.ZetaBlockDimensions); - if (ZA == 0 || Format == 0 || !ZetaEnable) + int GobBlockHeight = 1 << ((BlockDim >> 4) & 7); + + GalMemoryLayout Layout = (GalMemoryLayout)((BlockDim >> 12) & 1); //? + + bool ZetaEnable = ReadRegisterBool(NvGpuEngine3dReg.ZetaEnable); + + if (VA == 0 || ZetaFormat == 0 || !ZetaEnable) { Gpu.Renderer.RenderTarget.UnbindZeta(); return; } - long Key = Vmm.GetPhysicalAddress(ZA); + long Key = Vmm.GetPhysicalAddress(VA); int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz); int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert); - GalImageFormat ImageFormat = ImageUtils.ConvertZeta((GalZetaFormat)Format); + GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat); - GalImage Image = new GalImage(Width, Height, ImageFormat); + GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); - long Size = ImageUtils.GetSize(Image); - - Gpu.Renderer.Texture.CreateFb(Key, Size, Image); - - Gpu.Renderer.RenderTarget.BindZeta(Key); + Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image); } private long[] UploadShaders(NvGpuVmm Vmm) @@ -322,12 +338,6 @@ namespace Ryujinx.Graphics throw new ArgumentOutOfRangeException(nameof(Program)); } - private void SetFlip(GalPipelineState State) - { - State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); - State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); - } - private void SetFrontFace(GalPipelineState State) { float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); @@ -340,13 +350,8 @@ namespace Ryujinx.Graphics { switch (FrontFace) { - case GalFrontFace.CW: - FrontFace = GalFrontFace.CCW; - break; - - case GalFrontFace.CCW: - FrontFace = GalFrontFace.CW; - break; + case GalFrontFace.CW: FrontFace = GalFrontFace.CCW; break; + case GalFrontFace.CCW: FrontFace = GalFrontFace.CW; break; } } @@ -355,7 +360,7 @@ namespace Ryujinx.Graphics private void SetCullFace(GalPipelineState State) { - State.CullFaceEnabled = (ReadRegister(NvGpuEngine3dReg.CullFaceEnable) & 1) != 0; + State.CullFaceEnabled = ReadRegisterBool(NvGpuEngine3dReg.CullFaceEnable); if (State.CullFaceEnabled) { @@ -365,17 +370,22 @@ namespace Ryujinx.Graphics private void SetDepth(GalPipelineState State) { - State.DepthTestEnabled = (ReadRegister(NvGpuEngine3dReg.DepthTestEnable) & 1) != 0; + State.DepthTestEnabled = ReadRegisterBool(NvGpuEngine3dReg.DepthTestEnable); + + State.DepthWriteEnabled = ReadRegisterBool(NvGpuEngine3dReg.DepthWriteEnable); if (State.DepthTestEnabled) { State.DepthFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); } + + State.DepthRangeNear = ReadRegisterFloat(NvGpuEngine3dReg.DepthRangeNNear); + State.DepthRangeFar = ReadRegisterFloat(NvGpuEngine3dReg.DepthRangeNFar); } private void SetStencil(GalPipelineState State) { - State.StencilTestEnabled = (ReadRegister(NvGpuEngine3dReg.StencilEnable) & 1) != 0; + State.StencilTestEnabled = ReadRegisterBool(NvGpuEngine3dReg.StencilEnable); if (State.StencilTestEnabled) { @@ -397,27 +407,81 @@ namespace Ryujinx.Graphics } } - private void SetAlphaBlending(GalPipelineState State) + private void SetBlending(GalPipelineState State) { - //TODO: Support independent blend properly. - State.BlendEnabled = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; + bool BlendIndependent = ReadRegisterBool(NvGpuEngine3dReg.BlendIndependent); - if (State.BlendEnabled) + State.BlendIndependent = BlendIndependent; + + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) { - State.BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0; + if (BlendIndependent) + { + State.Blends[Index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable + Index); - State.BlendEquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb); - State.BlendFuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb); - State.BlendFuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb); - State.BlendEquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha); - State.BlendFuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); - State.BlendFuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); + if (State.Blends[Index].Enabled) + { + State.Blends[Index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.IBlendNSeparateAlpha + Index * 8); + + State.Blends[Index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationRgb + Index * 8); + State.Blends[Index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcRgb + Index * 8); + State.Blends[Index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstRgb + Index * 8); + State.Blends[Index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationAlpha + Index * 8); + State.Blends[Index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcAlpha + Index * 8); + State.Blends[Index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstAlpha + Index * 8); + } + } + else + { + //It seems that even when independent blend is disabled, the first IBlend enable + //register is still set to indicate whenever blend is enabled or not (?). + State.Blends[Index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable); + + if (State.Blends[Index].Enabled) + { + State.Blends[Index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.BlendSeparateAlpha); + + State.Blends[Index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationRgb); + State.Blends[Index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcRgb); + State.Blends[Index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstRgb); + State.Blends[Index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationAlpha); + State.Blends[Index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcAlpha); + State.Blends[Index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstAlpha); + } + } + } + } + + private GalBlendEquation ReadBlendEquation(NvGpuEngine3dReg Register) + { + return (GalBlendEquation)ReadRegister(Register); + } + + private GalBlendFactor ReadBlendFactor(NvGpuEngine3dReg Register) + { + return (GalBlendFactor)ReadRegister(Register); + } + + private void SetColorMask(GalPipelineState State) + { + bool ColorMaskCommon = ReadRegisterBool(NvGpuEngine3dReg.ColorMaskCommon); + + State.ColorMaskCommon = ColorMaskCommon; + + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + { + int ColorMask = ReadRegister(NvGpuEngine3dReg.ColorMaskN + (ColorMaskCommon ? 0 : Index)); + + State.ColorMasks[Index].Red = ((ColorMask >> 0) & 0xf) != 0; + State.ColorMasks[Index].Green = ((ColorMask >> 4) & 0xf) != 0; + State.ColorMasks[Index].Blue = ((ColorMask >> 8) & 0xf) != 0; + State.ColorMasks[Index].Alpha = ((ColorMask >> 12) & 0xf) != 0; } } private void SetPrimitiveRestart(GalPipelineState State) { - State.PrimitiveRestartEnabled = (ReadRegister(NvGpuEngine3dReg.PrimRestartEnable) & 1) != 0; + State.PrimitiveRestartEnabled = ReadRegisterBool(NvGpuEngine3dReg.PrimRestartEnable); if (State.PrimitiveRestartEnabled) { @@ -427,21 +491,22 @@ namespace Ryujinx.Graphics private void SetRenderTargets() { - bool SeparateFragData = (ReadRegister(NvGpuEngine3dReg.RTSeparateFragData) & 1) != 0; + //Commercial games do not seem to + //bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); - if (SeparateFragData) + uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl)); + + uint Count = Control & 0xf; + + if (Count > 0) { - uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl)); - - uint Count = Control & 0xf; - int[] Map = new int[Count]; - for (int i = 0; i < Count; i++) + for (int Index = 0; Index < Count; Index++) { - int Shift = 4 + i * 3; + int Shift = 4 + Index * 3; - Map[i] = (int)((Control >> Shift) & 7); + Map[Index] = (int)((Control >> Shift) & 7); } Gpu.Renderer.RenderTarget.SetMap(Map); @@ -488,15 +553,16 @@ namespace Ryujinx.Graphics { if (TextureHandle == 0) { - //TODO: Is this correct? - //Some games like puyo puyo will have 0 handles. - //It may be just normal behaviour or a bug caused by sync issues. - //The game does initialize the value properly after through. + //FIXME: Some games like puyo puyo will use handles with the value 0. + //This is a bug, most likely caused by sync issues. return; } + bool LinkedTsc = ReadRegisterBool(NvGpuEngine3dReg.LinkedTsc); + int TicIndex = (TextureHandle >> 0) & 0xfffff; - int TscIndex = (TextureHandle >> 20) & 0xfff; + + int TscIndex = LinkedTsc ? TicIndex : (TextureHandle >> 20) & 0xfff; long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); @@ -504,53 +570,30 @@ namespace Ryujinx.Graphics TicPosition += TicIndex * 0x20; TscPosition += TscIndex * 0x20; + GalImage Image = TextureFactory.MakeTexture(Vmm, TicPosition); + GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition); long Key = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; + if (Image.Layout == GalMemoryLayout.BlockLinear) + { + Key &= ~0x1ffL; + } + else if (Image.Layout == GalMemoryLayout.Pitch) + { + Key &= ~0x1fL; + } + Key = Vmm.GetPhysicalAddress(Key); if (Key == -1) { - //FIXME: Should'nt ignore invalid addresses. + //FIXME: Shouldn't ignore invalid addresses. return; } - if (IsFrameBufferPosition(Key)) - { - //This texture is a frame buffer texture, - //we shouldn't read anything from memory and bind - //the frame buffer texture instead, since we're not - //really writing anything to memory. - Gpu.Renderer.RenderTarget.BindTexture(Key, TexIndex); - } - else - { - GalImage NewImage = TextureFactory.MakeTexture(Vmm, TicPosition); - - long Size = (uint)ImageUtils.GetSize(NewImage); - - bool HasCachedTexture = false; - - if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalImage Image)) - { - if (NewImage.Equals(Image) && !QueryKeyUpload(Vmm, Key, Size, NvGpuBufferType.Texture)) - { - Gpu.Renderer.Texture.Bind(Key, TexIndex); - - HasCachedTexture = true; - } - } - - if (!HasCachedTexture) - { - byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition); - - Gpu.Renderer.Texture.Create(Key, Data, NewImage); - } - - Gpu.Renderer.Texture.Bind(Key, TexIndex); - } + Gpu.ResourceManager.SendTexture(Vmm, Key, Image, TexIndex); Gpu.Renderer.Texture.SetSampler(Sampler); } @@ -570,7 +613,7 @@ namespace Ryujinx.Graphics long Key = Vmm.GetPhysicalAddress(Cb.Position); - if (QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) + if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) { IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size); @@ -584,12 +627,15 @@ namespace Ryujinx.Graphics private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State) { - long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); + long IbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); - long IboKey = Vmm.GetPhysicalAddress(IndexPosition); + long IboKey = Vmm.GetPhysicalAddress(IbPosition); int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); + int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); + + GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt; @@ -597,7 +643,7 @@ namespace Ryujinx.Graphics if (IndexEntrySize > 4) { - throw new InvalidOperationException(); + throw new InvalidOperationException("Invalid index entry size \"" + IndexEntrySize + "\"!"); } if (IndexCount != 0) @@ -606,14 +652,50 @@ namespace Ryujinx.Graphics bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize); - if (!IboCached || QueryKeyUpload(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index)) - { - IntPtr DataAddress = Vmm.GetHostAddress(IndexPosition, IbSize); + bool UsesLegacyQuads = + PrimType == GalPrimitiveType.Quads || + PrimType == GalPrimitiveType.QuadStrip; - Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress); + if (!IboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index)) + { + if (!UsesLegacyQuads) + { + IntPtr DataAddress = Vmm.GetHostAddress(IbPosition, IbSize); + + Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress); + } + else + { + byte[] Buffer = Vmm.ReadBytes(IbPosition, IbSize); + + if (PrimType == GalPrimitiveType.Quads) + { + Buffer = QuadHelper.ConvertIbQuadsToTris(Buffer, IndexEntrySize, IndexCount); + } + else /* if (PrimType == GalPrimitiveType.QuadStrip) */ + { + Buffer = QuadHelper.ConvertIbQuadStripToTris(Buffer, IndexEntrySize, IndexCount); + } + + Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Buffer); + } } - Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat); + if (!UsesLegacyQuads) + { + Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat); + } + else + { + if (PrimType == GalPrimitiveType.Quads) + { + Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadsToTris(IbSize), IndexFormat); + } + else /* if (PrimType == GalPrimitiveType.QuadStrip) */ + { + Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadStripToTris(IbSize), IndexFormat); + } + } } List[] Attribs = new List[32]; @@ -629,10 +711,19 @@ namespace Ryujinx.Graphics Attribs[ArrayIndex] = new List(); } + long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4); + + int Offset = (Packed >> 7) & 0x3fff; + + //Note: 16 is the maximum size of an attribute, + //having a component size of 32-bits with 4 elements (a vec4). + IntPtr Pointer = Vmm.GetHostAddress(VertexPosition + Offset, 16); + Attribs[ArrayIndex].Add(new GalVertexAttrib( Attr, ((Packed >> 6) & 0x1) != 0, - (Packed >> 7) & 0x3fff, + Offset, + Pointer, (GalVertexAttribSize)((Packed >> 21) & 0x3f), (GalVertexAttribType)((Packed >> 27) & 0x7), ((Packed >> 31) & 0x1) != 0)); @@ -661,7 +752,7 @@ namespace Ryujinx.Graphics int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4); - bool Instanced = (ReadRegister(NvGpuEngine3dReg.VertexArrayNInstance + Index) & 1) != 0; + bool Instanced = ReadRegisterBool(NvGpuEngine3dReg.VertexArrayNInstance + Index); int Stride = Control & 0xfff; @@ -682,7 +773,7 @@ namespace Ryujinx.Graphics bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize); - if (!VboCached || QueryKeyUpload(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex)) + if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex)) { IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize); @@ -726,6 +817,8 @@ namespace Ryujinx.Graphics Gpu.Renderer.Pipeline.Bind(State); + Gpu.Renderer.RenderTarget.Bind(); + if (IndexCount != 0) { int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); @@ -736,6 +829,27 @@ namespace Ryujinx.Graphics long IboKey = Vmm.GetPhysicalAddress(IndexPosition); + //Quad primitive types were deprecated on OpenGL 3.x, + //they are converted to a triangles index buffer on IB creation, + //so we should use the triangles type here too. + if (PrimType == GalPrimitiveType.Quads || + PrimType == GalPrimitiveType.QuadStrip) + { + PrimType = GalPrimitiveType.Triangles; + + //Note: We assume that index first points to the first + //vertex of a quad, if it points to the middle of a + //quad (First % 4 != 0 for Quads) then it will not work properly. + if (PrimType == GalPrimitiveType.Quads) + { + IndexFirst = QuadHelper.ConvertIbSizeQuadsToTris(IndexFirst); + } + else /* if (PrimType == GalPrimitiveType.QuadStrip) */ + { + IndexFirst = QuadHelper.ConvertIbSizeQuadStripToTris(IndexFirst); + } + } + Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType); } else @@ -758,9 +872,9 @@ namespace Ryujinx.Graphics WriteCounterAndTimestamp } - private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + private void QueryControl(NvGpuVmm Vmm, GpuMethodCall MethCall) { - WriteRegister(PBEntry); + WriteRegister(MethCall); long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress); @@ -778,7 +892,7 @@ namespace Ryujinx.Graphics //TODO: Implement counters. long Counter = 1; - long Timestamp = (uint)Environment.TickCount; + long Timestamp = PerformanceCounter.ElapsedMilliseconds; Timestamp = (long)(Timestamp * 615384.615385); @@ -790,29 +904,24 @@ namespace Ryujinx.Graphics } } - private void CbData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + private void CbData(NvGpuVmm Vmm, GpuMethodCall MethCall) { long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset); - foreach (int Arg in PBEntry.Arguments) - { - Vmm.WriteInt32(Position + Offset, Arg); + Vmm.WriteInt32(Position + Offset, MethCall.Argument); - Offset += 4; - } + WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset + 4); - WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset); - - UploadedKeys[(int)NvGpuBufferType.ConstBuffer].Clear(); + Gpu.ResourceManager.ClearPbCache(NvGpuBufferType.ConstBuffer); } - private void CbBind(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + private void CbBind(NvGpuVmm Vmm, GpuMethodCall MethCall) { - int Stage = (PBEntry.Method - 0x904) >> 3; + int Stage = (MethCall.Method - 0x904) >> 3; - int Index = PBEntry.Arguments[0]; + int Index = MethCall.Argument; bool Enabled = (Index & 1) != 0; @@ -851,14 +960,9 @@ namespace Ryujinx.Graphics (uint)Registers[(int)Reg + 1]; } - private void WriteRegister(NvGpuPBEntry PBEntry) + private void WriteRegister(GpuMethodCall MethCall) { - int ArgsCount = PBEntry.Arguments.Count; - - if (ArgsCount > 0) - { - Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1]; - } + Registers[MethCall.Method] = MethCall.Argument; } private int ReadRegister(NvGpuEngine3dReg Reg) @@ -871,28 +975,14 @@ namespace Ryujinx.Graphics return BitConverter.Int32BitsToSingle(ReadRegister(Reg)); } + private bool ReadRegisterBool(NvGpuEngine3dReg Reg) + { + return (ReadRegister(Reg) & 1) != 0; + } + private void WriteRegister(NvGpuEngine3dReg Reg, int Value) { Registers[(int)Reg] = Value; } - - public bool IsFrameBufferPosition(long Position) - { - return FrameBuffers.Contains(Position); - } - - private bool QueryKeyUpload(NvGpuVmm Vmm, long Key, long Size, NvGpuBufferType Type) - { - List Uploaded = UploadedKeys[(int)Type]; - - if (Uploaded.Contains(Key)) - { - return false; - } - - Uploaded.Add(Key); - - return Vmm.IsRegionModified(Key, Size, Type); - } } } diff --git a/Ryujinx.Graphics/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/NvGpuEngine3dReg.cs index f96e711500..c229e6c290 100644 --- a/Ryujinx.Graphics/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/NvGpuEngine3dReg.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Graphics FrameBufferNWidth = 0x202, FrameBufferNHeight = 0x203, FrameBufferNFormat = 0x204, + FrameBufferNBlockDim = 0x205, ViewportNScaleX = 0x280, ViewportNScaleY = 0x281, ViewportNScaleZ = 0x282, @@ -14,6 +15,8 @@ namespace Ryujinx.Graphics ViewportNTranslateZ = 0x285, ViewportNHoriz = 0x300, ViewportNVert = 0x301, + DepthRangeNNear = 0x302, + DepthRangeNFar = 0x303, VertexArrayFirst = 0x35d, VertexArrayCount = 0x35e, ClearNColor = 0x360, @@ -22,6 +25,7 @@ namespace Ryujinx.Graphics StencilBackFuncRef = 0x3d5, StencilBackMask = 0x3d6, StencilBackFuncMask = 0x3d7, + ColorMaskCommon = 0x3e4, RTSeparateFragData = 0x3eb, ZetaAddress = 0x3f8, ZetaFormat = 0x3fa, @@ -32,8 +36,10 @@ namespace Ryujinx.Graphics ZetaHoriz = 0x48a, ZetaVert = 0x48b, ZetaArrayMode = 0x48c, + LinkedTsc = 0x48d, DepthTestEnable = 0x4b3, - IBlendEnable = 0x4b9, + BlendIndependent = 0x4b9, + DepthWriteEnable = 0x4ba, DepthTestFunction = 0x4c3, BlendSeparateAlpha = 0x4cf, BlendEquationRgb = 0x4d0, @@ -42,7 +48,7 @@ namespace Ryujinx.Graphics BlendEquationAlpha = 0x4d3, BlendFuncSrcAlpha = 0x4d4, BlendFuncDstAlpha = 0x4d6, - BlendEnableMaster = 0x4d7, + BlendEnable = 0x4d7, IBlendNEnable = 0x4d8, StencilEnable = 0x4e0, StencilFrontOpFail = 0x4e1, @@ -52,6 +58,7 @@ namespace Ryujinx.Graphics StencilFrontFuncRef = 0x4e5, StencilFrontFuncMask = 0x4e6, StencilFrontMask = 0x4e7, + ScreenYControl = 0x4eb, VertexArrayElemBase = 0x50d, VertexArrayInstBase = 0x50e, ZetaEnable = 0x54e, @@ -62,6 +69,7 @@ namespace Ryujinx.Graphics StencilBackOpZFail = 0x567, StencilBackOpZPass = 0x568, StencilBackFuncFunc = 0x569, + FrameBufferSrgb = 0x56e, ShaderAddress = 0x582, VertexBeginGl = 0x586, PrimRestartEnable = 0x591, @@ -75,6 +83,7 @@ namespace Ryujinx.Graphics CullFaceEnable = 0x646, FrontFace = 0x647, CullFace = 0x648, + ColorMaskN = 0x680, QueryAddress = 0x6c0, QuerySequence = 0x6c2, QueryControl = 0x6c3, diff --git a/Ryujinx.Graphics/NvGpuEngineDma.cs b/Ryujinx.Graphics/NvGpuEngineDma.cs deleted file mode 100644 index 04be742f85..0000000000 --- a/Ryujinx.Graphics/NvGpuEngineDma.cs +++ /dev/null @@ -1,143 +0,0 @@ -using Ryujinx.Graphics.Memory; -using Ryujinx.Graphics.Texture; -using System.Collections.Generic; - -namespace Ryujinx.Graphics -{ - public class NvGpuEngineDma : INvGpuEngine - { - public int[] Registers { get; private set; } - - private NvGpu Gpu; - - private Dictionary Methods; - - public NvGpuEngineDma(NvGpu Gpu) - { - this.Gpu = Gpu; - - Registers = new int[0x1d6]; - - Methods = new Dictionary(); - - void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) - { - while (Count-- > 0) - { - Methods.Add(Meth, Method); - - Meth += Stride; - } - } - - AddMethod(0xc0, 1, 1, Execute); - } - - public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) - { - if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) - { - Method(Vmm, PBEntry); - } - else - { - WriteRegister(PBEntry); - } - } - - private void Execute(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) - { - int Control = PBEntry.Arguments[0]; - - bool SrcLinear = ((Control >> 7) & 1) != 0; - bool DstLinear = ((Control >> 8) & 1) != 0; - - long SrcAddress = MakeInt64From2xInt32(NvGpuEngineDmaReg.SrcAddress); - long DstAddress = MakeInt64From2xInt32(NvGpuEngineDmaReg.DstAddress); - - int SrcPitch = ReadRegister(NvGpuEngineDmaReg.SrcPitch); - int DstPitch = ReadRegister(NvGpuEngineDmaReg.DstPitch); - - int DstBlkDim = ReadRegister(NvGpuEngineDmaReg.DstBlkDim); - int DstSizeX = ReadRegister(NvGpuEngineDmaReg.DstSizeX); - int DstSizeY = ReadRegister(NvGpuEngineDmaReg.DstSizeY); - int DstSizeZ = ReadRegister(NvGpuEngineDmaReg.DstSizeZ); - int DstPosXY = ReadRegister(NvGpuEngineDmaReg.DstPosXY); - int DstPosZ = ReadRegister(NvGpuEngineDmaReg.DstPosZ); - - int SrcBlkDim = ReadRegister(NvGpuEngineDmaReg.SrcBlkDim); - int SrcSizeX = ReadRegister(NvGpuEngineDmaReg.SrcSizeX); - int SrcSizeY = ReadRegister(NvGpuEngineDmaReg.SrcSizeY); - int SrcSizeZ = ReadRegister(NvGpuEngineDmaReg.SrcSizeZ); - int SrcPosXY = ReadRegister(NvGpuEngineDmaReg.SrcPosXY); - int SrcPosZ = ReadRegister(NvGpuEngineDmaReg.SrcPosZ); - - int DstPosX = (DstPosXY >> 0) & 0xffff; - int DstPosY = (DstPosXY >> 16) & 0xffff; - - int SrcPosX = (SrcPosXY >> 0) & 0xffff; - int SrcPosY = (SrcPosXY >> 16) & 0xffff; - - int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf); - int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); - - ISwizzle SrcSwizzle; - - if (SrcLinear) - { - SrcSwizzle = new LinearSwizzle(SrcPitch, 1); - } - else - { - SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, 1, SrcBlockHeight); - } - - ISwizzle DstSwizzle; - - if (DstLinear) - { - DstSwizzle = new LinearSwizzle(DstPitch, 1); - } - else - { - DstSwizzle = new BlockLinearSwizzle(DstSizeX, 1, DstBlockHeight); - } - - for (int Y = 0; Y < DstSizeY; Y++) - for (int X = 0; X < DstSizeX; X++) - { - long SrcOffset = SrcAddress + (uint)SrcSwizzle.GetSwizzleOffset(X, Y); - long DstOffset = DstAddress + (uint)DstSwizzle.GetSwizzleOffset(X, Y); - - Vmm.WriteByte(DstOffset, Vmm.ReadByte(SrcOffset)); - } - } - - private long MakeInt64From2xInt32(NvGpuEngineDmaReg Reg) - { - return - (long)Registers[(int)Reg + 0] << 32 | - (uint)Registers[(int)Reg + 1]; - } - - private void WriteRegister(NvGpuPBEntry PBEntry) - { - int ArgsCount = PBEntry.Arguments.Count; - - if (ArgsCount > 0) - { - Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1]; - } - } - - private int ReadRegister(NvGpuEngineDmaReg Reg) - { - return Registers[(int)Reg]; - } - - private void WriteRegister(NvGpuEngineDmaReg Reg, int Value) - { - Registers[(int)Reg] = Value; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuEngineM2mf.cs b/Ryujinx.Graphics/NvGpuEngineM2mf.cs new file mode 100644 index 0000000000..5ee18ea9e8 --- /dev/null +++ b/Ryujinx.Graphics/NvGpuEngineM2mf.cs @@ -0,0 +1,187 @@ +using Ryujinx.Graphics.Memory; +using Ryujinx.Graphics.Texture; +using System.Collections.Generic; + +namespace Ryujinx.Graphics +{ + class NvGpuEngineM2mf : INvGpuEngine + { + public int[] Registers { get; private set; } + + private NvGpu Gpu; + + private Dictionary Methods; + + public NvGpuEngineM2mf(NvGpu Gpu) + { + this.Gpu = Gpu; + + Registers = new int[0x1d6]; + + Methods = new Dictionary(); + + void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) + { + while (Count-- > 0) + { + Methods.Add(Meth, Method); + + Meth += Stride; + } + } + + AddMethod(0xc0, 1, 1, Execute); + } + + public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + { + if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method)) + { + Method(Vmm, MethCall); + } + else + { + WriteRegister(MethCall); + } + } + + private void Execute(NvGpuVmm Vmm, GpuMethodCall MethCall) + { + //TODO: Some registers and copy modes are still not implemented. + int Control = MethCall.Argument; + + bool SrcLinear = ((Control >> 7) & 1) != 0; + bool DstLinear = ((Control >> 8) & 1) != 0; + bool Copy2d = ((Control >> 9) & 1) != 0; + + long SrcAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.SrcAddress); + long DstAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.DstAddress); + + int SrcPitch = ReadRegister(NvGpuEngineM2mfReg.SrcPitch); + int DstPitch = ReadRegister(NvGpuEngineM2mfReg.DstPitch); + + int XCount = ReadRegister(NvGpuEngineM2mfReg.XCount); + int YCount = ReadRegister(NvGpuEngineM2mfReg.YCount); + + int Swizzle = ReadRegister(NvGpuEngineM2mfReg.Swizzle); + + int DstBlkDim = ReadRegister(NvGpuEngineM2mfReg.DstBlkDim); + int DstSizeX = ReadRegister(NvGpuEngineM2mfReg.DstSizeX); + int DstSizeY = ReadRegister(NvGpuEngineM2mfReg.DstSizeY); + int DstSizeZ = ReadRegister(NvGpuEngineM2mfReg.DstSizeZ); + int DstPosXY = ReadRegister(NvGpuEngineM2mfReg.DstPosXY); + int DstPosZ = ReadRegister(NvGpuEngineM2mfReg.DstPosZ); + + int SrcBlkDim = ReadRegister(NvGpuEngineM2mfReg.SrcBlkDim); + int SrcSizeX = ReadRegister(NvGpuEngineM2mfReg.SrcSizeX); + int SrcSizeY = ReadRegister(NvGpuEngineM2mfReg.SrcSizeY); + int SrcSizeZ = ReadRegister(NvGpuEngineM2mfReg.SrcSizeZ); + int SrcPosXY = ReadRegister(NvGpuEngineM2mfReg.SrcPosXY); + int SrcPosZ = ReadRegister(NvGpuEngineM2mfReg.SrcPosZ); + + int SrcCpp = ((Swizzle >> 20) & 7) + 1; + int DstCpp = ((Swizzle >> 24) & 7) + 1; + + int DstPosX = (DstPosXY >> 0) & 0xffff; + int DstPosY = (DstPosXY >> 16) & 0xffff; + + int SrcPosX = (SrcPosXY >> 0) & 0xffff; + int SrcPosY = (SrcPosXY >> 16) & 0xffff; + + int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf); + int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); + + long SrcPA = Vmm.GetPhysicalAddress(SrcAddress); + long DstPA = Vmm.GetPhysicalAddress(DstAddress); + + if (Copy2d) + { + if (SrcLinear) + { + SrcPosX = SrcPosY = SrcPosZ = 0; + } + + if (DstLinear) + { + DstPosX = DstPosY = DstPosZ = 0; + } + + if (SrcLinear && DstLinear) + { + for (int Y = 0; Y < YCount; Y++) + { + int SrcOffset = (SrcPosY + Y) * SrcPitch + SrcPosX * SrcCpp; + int DstOffset = (DstPosY + Y) * DstPitch + DstPosX * DstCpp; + + long Src = SrcPA + (uint)SrcOffset; + long Dst = DstPA + (uint)DstOffset; + + Vmm.Memory.CopyBytes(Src, Dst, XCount * SrcCpp); + } + } + else + { + ISwizzle SrcSwizzle; + + if (SrcLinear) + { + SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp); + } + else + { + SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, SrcCpp, SrcBlockHeight); + } + + ISwizzle DstSwizzle; + + if (DstLinear) + { + DstSwizzle = new LinearSwizzle(DstPitch, DstCpp); + } + else + { + DstSwizzle = new BlockLinearSwizzle(DstSizeX, DstCpp, DstBlockHeight); + } + + for (int Y = 0; Y < YCount; Y++) + for (int X = 0; X < XCount; X++) + { + int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y); + int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y); + + long Src = SrcPA + (uint)SrcOffset; + long Dst = DstPA + (uint)DstOffset; + + Vmm.Memory.CopyBytes(Src, Dst, SrcCpp); + } + } + } + else + { + Vmm.Memory.CopyBytes(SrcPA, DstPA, XCount); + } + } + + private long MakeInt64From2xInt32(NvGpuEngineM2mfReg Reg) + { + return + (long)Registers[(int)Reg + 0] << 32 | + (uint)Registers[(int)Reg + 1]; + } + + private void WriteRegister(GpuMethodCall MethCall) + { + Registers[MethCall.Method] = MethCall.Argument; + } + + private int ReadRegister(NvGpuEngineM2mfReg Reg) + { + return Registers[(int)Reg]; + } + + private void WriteRegister(NvGpuEngineM2mfReg Reg, int Value) + { + Registers[(int)Reg] = Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuEngineDmaReg.cs b/Ryujinx.Graphics/NvGpuEngineM2mfReg.cs similarity index 81% rename from Ryujinx.Graphics/NvGpuEngineDmaReg.cs rename to Ryujinx.Graphics/NvGpuEngineM2mfReg.cs index b0fa1fbf37..170e0b7b64 100644 --- a/Ryujinx.Graphics/NvGpuEngineDmaReg.cs +++ b/Ryujinx.Graphics/NvGpuEngineM2mfReg.cs @@ -1,11 +1,14 @@ namespace Ryujinx.Graphics { - enum NvGpuEngineDmaReg + enum NvGpuEngineM2mfReg { SrcAddress = 0x100, DstAddress = 0x102, SrcPitch = 0x104, DstPitch = 0x105, + XCount = 0x106, + YCount = 0x107, + Swizzle = 0x1c2, DstBlkDim = 0x1c3, DstSizeX = 0x1c4, DstSizeY = 0x1c5, diff --git a/Ryujinx.Graphics/NvGpuEngineP2mf.cs b/Ryujinx.Graphics/NvGpuEngineP2mf.cs new file mode 100644 index 0000000000..b111c59e32 --- /dev/null +++ b/Ryujinx.Graphics/NvGpuEngineP2mf.cs @@ -0,0 +1,161 @@ +using Ryujinx.Graphics.Memory; +using Ryujinx.Graphics.Texture; +using System.Collections.Generic; + +namespace Ryujinx.Graphics +{ + class NvGpuEngineP2mf : INvGpuEngine + { + public int[] Registers { get; private set; } + + private NvGpu Gpu; + + private Dictionary Methods; + + private int CopyStartX; + private int CopyStartY; + + private int CopyWidth; + private int CopyHeight; + private int CopyGobBlockHeight; + + private long CopyAddress; + + private int CopyOffset; + private int CopySize; + + private bool CopyLinear; + + private byte[] Buffer; + + public NvGpuEngineP2mf(NvGpu Gpu) + { + this.Gpu = Gpu; + + Registers = new int[0x80]; + + Methods = new Dictionary(); + + void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) + { + while (Count-- > 0) + { + Methods.Add(Meth, Method); + + Meth += Stride; + } + } + + AddMethod(0x6c, 1, 1, Execute); + AddMethod(0x6d, 1, 1, PushData); + } + + public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + { + if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method)) + { + Method(Vmm, MethCall); + } + else + { + WriteRegister(MethCall); + } + } + + private void Execute(NvGpuVmm Vmm, GpuMethodCall MethCall) + { + //TODO: Some registers and copy modes are still not implemented. + int Control = MethCall.Argument; + + long DstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress); + + int DstPitch = ReadRegister(NvGpuEngineP2mfReg.DstPitch); + int DstBlkDim = ReadRegister(NvGpuEngineP2mfReg.DstBlockDim); + + int DstX = ReadRegister(NvGpuEngineP2mfReg.DstX); + int DstY = ReadRegister(NvGpuEngineP2mfReg.DstY); + + int DstWidth = ReadRegister(NvGpuEngineP2mfReg.DstWidth); + int DstHeight = ReadRegister(NvGpuEngineP2mfReg.DstHeight); + + int LineLengthIn = ReadRegister(NvGpuEngineP2mfReg.LineLengthIn); + int LineCount = ReadRegister(NvGpuEngineP2mfReg.LineCount); + + CopyLinear = (Control & 1) != 0; + + CopyGobBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); + + CopyStartX = DstX; + CopyStartY = DstY; + + CopyWidth = DstWidth; + CopyHeight = DstHeight; + + CopyAddress = DstAddress; + + CopyOffset = 0; + CopySize = LineLengthIn * LineCount; + + Buffer = new byte[CopySize]; + } + + private void PushData(NvGpuVmm Vmm, GpuMethodCall MethCall) + { + if (Buffer == null) + { + return; + } + + for (int Shift = 0; Shift < 32 && CopyOffset < CopySize; Shift += 8, CopyOffset++) + { + Buffer[CopyOffset] = (byte)(MethCall.Argument >> Shift); + } + + if (MethCall.IsLastCall) + { + if (CopyLinear) + { + Vmm.WriteBytes(CopyAddress, Buffer); + } + else + { + BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(CopyWidth, 1, CopyGobBlockHeight); + + int SrcOffset = 0; + + for (int Y = CopyStartY; Y < CopyHeight && SrcOffset < CopySize; Y++) + for (int X = CopyStartX; X < CopyWidth && SrcOffset < CopySize; X++) + { + int DstOffset = Swizzle.GetSwizzleOffset(X, Y); + + Vmm.WriteByte(CopyAddress + DstOffset, Buffer[SrcOffset++]); + } + } + + Buffer = null; + } + } + + private long MakeInt64From2xInt32(NvGpuEngineP2mfReg Reg) + { + return + (long)Registers[(int)Reg + 0] << 32 | + (uint)Registers[(int)Reg + 1]; + } + + private void WriteRegister(GpuMethodCall MethCall) + { + Registers[MethCall.Method] = MethCall.Argument; + } + + private int ReadRegister(NvGpuEngineP2mfReg Reg) + { + return Registers[(int)Reg]; + } + + private void WriteRegister(NvGpuEngineP2mfReg Reg, int Value) + { + Registers[(int)Reg] = Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuEngineP2mfReg.cs b/Ryujinx.Graphics/NvGpuEngineP2mfReg.cs new file mode 100644 index 0000000000..803ed299b5 --- /dev/null +++ b/Ryujinx.Graphics/NvGpuEngineP2mfReg.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics +{ + enum NvGpuEngineP2mfReg + { + LineLengthIn = 0x60, + LineCount = 0x61, + DstAddress = 0x62, + DstPitch = 0x64, + DstBlockDim = 0x65, + DstWidth = 0x66, + DstHeight = 0x67, + DstDepth = 0x68, + DstZ = 0x69, + DstX = 0x6a, + DstY = 0x6b + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuFifo.cs b/Ryujinx.Graphics/NvGpuFifo.cs index 3b79a055e4..a8d1b36d17 100644 --- a/Ryujinx.Graphics/NvGpuFifo.cs +++ b/Ryujinx.Graphics/NvGpuFifo.cs @@ -1,10 +1,8 @@ using Ryujinx.Graphics.Memory; -using System.Collections.Concurrent; -using System.Threading; namespace Ryujinx.Graphics { - public class NvGpuFifo + class NvGpuFifo { private const int MacrosCount = 0x80; private const int MacroIndexMask = MacrosCount - 1; @@ -15,33 +13,47 @@ namespace Ryujinx.Graphics private NvGpu Gpu; - private ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry[])> BufferQueue; - private NvGpuEngine[] SubChannels; - public AutoResetEvent Event { get; private set; } - private struct CachedMacro { public int Position { get; private set; } + private bool ExecutionPending; + private int Argument; + private MacroInterpreter Interpreter; public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, int Position) { this.Position = Position; + ExecutionPending = false; + Argument = 0; + Interpreter = new MacroInterpreter(PFifo, Engine); } - public void PushParam(int Param) + public void StartExecution(int Argument) { - Interpreter?.Fifo.Enqueue(Param); + this.Argument = Argument; + + ExecutionPending = true; } - public void Execute(NvGpuVmm Vmm, int[] Mme, int Param) + public void Execute(NvGpuVmm Vmm, int[] Mme) { - Interpreter?.Execute(Vmm, Mme, Position, Param); + if (ExecutionPending) + { + ExecutionPending = false; + + Interpreter?.Execute(Vmm, Mme, Position, Argument); + } + } + + public void PushArgument(int Argument) + { + Interpreter?.Fifo.Enqueue(Argument); } } @@ -56,143 +68,109 @@ namespace Ryujinx.Graphics { this.Gpu = Gpu; - BufferQueue = new ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry[])>(); - SubChannels = new NvGpuEngine[8]; Macros = new CachedMacro[MacrosCount]; Mme = new int[MmeWords]; - - Event = new AutoResetEvent(false); } - public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer) + public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) { - BufferQueue.Enqueue((Vmm, Buffer)); - - Event.Set(); - } - - public void DispatchCalls() - { - while (Step()); - } - - private (NvGpuVmm Vmm, NvGpuPBEntry[] Pb) Curr; - - private int CurrPbEntryIndex; - - public bool Step() - { - while (Curr.Pb == null || Curr.Pb.Length <= CurrPbEntryIndex) + if ((NvGpuFifoMeth)MethCall.Method == NvGpuFifoMeth.BindChannel) { - if (!BufferQueue.TryDequeue(out Curr)) - { - return false; - } + NvGpuEngine Engine = (NvGpuEngine)MethCall.Argument; - Gpu.Engine3d.ResetCache(); - - CurrPbEntryIndex = 0; + SubChannels[MethCall.SubChannel] = Engine; + } + else + { + switch (SubChannels[MethCall.SubChannel]) + { + case NvGpuEngine._2d: Call2dMethod (Vmm, MethCall); break; + case NvGpuEngine._3d: Call3dMethod (Vmm, MethCall); break; + case NvGpuEngine.P2mf: CallP2mfMethod(Vmm, MethCall); break; + case NvGpuEngine.M2mf: CallM2mfMethod(Vmm, MethCall); break; + } } - - CallMethod(Curr.Vmm, Curr.Pb[CurrPbEntryIndex++]); - - return true; } - private void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + private void Call2dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) { - if (PBEntry.Method < 0x80) + Gpu.Engine2d.CallMethod(Vmm, MethCall); + } + + private void Call3dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + { + if (MethCall.Method < 0x80) { - switch ((NvGpuFifoMeth)PBEntry.Method) + switch ((NvGpuFifoMeth)MethCall.Method) { - case NvGpuFifoMeth.BindChannel: - { - NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0]; - - SubChannels[PBEntry.SubChannel] = Engine; - - break; - } - case NvGpuFifoMeth.SetMacroUploadAddress: { - CurrMacroPosition = PBEntry.Arguments[0]; + CurrMacroPosition = MethCall.Argument; break; } case NvGpuFifoMeth.SendMacroCodeData: { - foreach (int Arg in PBEntry.Arguments) - { - Mme[CurrMacroPosition++] = Arg; - } + Mme[CurrMacroPosition++] = MethCall.Argument; + break; } case NvGpuFifoMeth.SetMacroBindingIndex: { - CurrMacroBindIndex = PBEntry.Arguments[0]; + CurrMacroBindIndex = MethCall.Argument; break; } case NvGpuFifoMeth.BindMacro: { - int Position = PBEntry.Arguments[0]; + int Position = MethCall.Argument; Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position); break; } + + default: CallP2mfMethod(Vmm, MethCall); break; } } + else if (MethCall.Method < 0xe00) + { + Gpu.Engine3d.CallMethod(Vmm, MethCall); + } else { - switch (SubChannels[PBEntry.SubChannel]) + int MacroIndex = (MethCall.Method >> 1) & MacroIndexMask; + + if ((MethCall.Method & 1) != 0) { - case NvGpuEngine._2d: Call2dMethod (Vmm, PBEntry); break; - case NvGpuEngine._3d: Call3dMethod (Vmm, PBEntry); break; - case NvGpuEngine.Dma: CallDmaMethod(Vmm, PBEntry); break; - } - } - } - - private void Call2dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) - { - Gpu.Engine2d.CallMethod(Vmm, PBEntry); - } - - private void Call3dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) - { - if (PBEntry.Method < 0xe00) - { - Gpu.Engine3d.CallMethod(Vmm, PBEntry); - } - else - { - int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask; - - if ((PBEntry.Method & 1) != 0) - { - foreach (int Arg in PBEntry.Arguments) - { - Macros[MacroIndex].PushParam(Arg); - } + Macros[MacroIndex].PushArgument(MethCall.Argument); } else { - Macros[MacroIndex].Execute(Vmm, Mme, PBEntry.Arguments[0]); + Macros[MacroIndex].StartExecution(MethCall.Argument); + } + + if (MethCall.IsLastCall) + { + Macros[MacroIndex].Execute(Vmm, Mme); } } } - private void CallDmaMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) + private void CallP2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) { - Gpu.EngineDma.CallMethod(Vmm, PBEntry); + Gpu.EngineP2mf.CallMethod(Vmm, MethCall); + } + + private void CallM2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + { + Gpu.EngineM2mf.CallMethod(Vmm, MethCall); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuMethod.cs b/Ryujinx.Graphics/NvGpuMethod.cs index 5babf2c32f..83f3312ae6 100644 --- a/Ryujinx.Graphics/NvGpuMethod.cs +++ b/Ryujinx.Graphics/NvGpuMethod.cs @@ -2,5 +2,5 @@ using Ryujinx.Graphics.Memory; namespace Ryujinx.Graphics { - delegate void NvGpuMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry); + delegate void NvGpuMethod(NvGpuVmm Vmm, GpuMethodCall MethCall); } \ No newline at end of file diff --git a/Ryujinx.Graphics/QuadHelper.cs b/Ryujinx.Graphics/QuadHelper.cs new file mode 100644 index 0000000000..0dfffce0bc --- /dev/null +++ b/Ryujinx.Graphics/QuadHelper.cs @@ -0,0 +1,81 @@ +using System; + +namespace Ryujinx.Graphics +{ + static class QuadHelper + { + public static int ConvertIbSizeQuadsToTris(int Size) + { + return Size <= 0 ? 0 : (Size / 4) * 6; + } + + public static int ConvertIbSizeQuadStripToTris(int Size) + { + return Size <= 1 ? 0 : ((Size - 2) / 2) * 6; + } + + public static byte[] ConvertIbQuadsToTris(byte[] Data, int EntrySize, int Count) + { + int PrimitivesCount = Count / 4; + + int QuadPrimSize = 4 * EntrySize; + int TrisPrimSize = 6 * EntrySize; + + byte[] Output = new byte[PrimitivesCount * 6 * EntrySize]; + + for (int Prim = 0; Prim < PrimitivesCount; Prim++) + { + void AssignIndex(int Src, int Dst, int CopyCount = 1) + { + Src = Prim * QuadPrimSize + Src * EntrySize; + Dst = Prim * TrisPrimSize + Dst * EntrySize; + + Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize); + } + + //0 1 2 -> 0 1 2. + AssignIndex(0, 0, 3); + + //2 3 -> 3 4. + AssignIndex(2, 3, 2); + + //0 -> 5. + AssignIndex(0, 5); + } + + return Output; + } + + public static byte[] ConvertIbQuadStripToTris(byte[] Data, int EntrySize, int Count) + { + int PrimitivesCount = (Count - 2) / 2; + + int QuadPrimSize = 2 * EntrySize; + int TrisPrimSize = 6 * EntrySize; + + byte[] Output = new byte[PrimitivesCount * 6 * EntrySize]; + + for (int Prim = 0; Prim < PrimitivesCount; Prim++) + { + void AssignIndex(int Src, int Dst, int CopyCount = 1) + { + Src = Prim * QuadPrimSize + Src * EntrySize + 2 * EntrySize; + Dst = Prim * TrisPrimSize + Dst * EntrySize; + + Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize); + } + + //-2 -1 0 -> 0 1 2. + AssignIndex(-2, 0, 3); + + //0 1 -> 3 4. + AssignIndex(0, 3, 2); + + //-2 -> 5. + AssignIndex(-2, 5); + } + + return Output; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj index 7d86cbe134..be113a21bf 100644 --- a/Ryujinx.Graphics/Ryujinx.Graphics.csproj +++ b/Ryujinx.Graphics/Ryujinx.Graphics.csproj @@ -19,6 +19,7 @@ + diff --git a/Ryujinx.Graphics/Texture/ImageConverter.cs b/Ryujinx.Graphics/Texture/ImageConverter.cs deleted file mode 100644 index 89529061fd..0000000000 --- a/Ryujinx.Graphics/Texture/ImageConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Ryujinx.Graphics.Texture -{ - static class ImageConverter - { - public static byte[] G8R8ToR8G8( - byte[] Data, - int Width, - int Height, - int Depth) - { - int Texels = Width * Height * Depth; - - byte[] Output = new byte[Texels * 2]; - - for (int Texel = 0; Texel < Texels; Texel++) - { - Output[Texel * 2 + 0] = Data[Texel * 2 + 1]; - Output[Texel * 2 + 1] = Data[Texel * 2 + 0]; - } - - return Output; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs index ccea43ec1c..89c29557e2 100644 --- a/Ryujinx.Graphics/Texture/ImageUtils.cs +++ b/Ryujinx.Graphics/Texture/ImageUtils.cs @@ -1,190 +1,206 @@ -using Ryujinx.Graphics.Gal; +using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Memory; using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Texture { - static class ImageUtils + public static class ImageUtils { - struct ImageDescriptor + [Flags] + private enum TargetBuffer { - public TextureReaderDelegate Reader; + Color = 1 << 0, + Depth = 1 << 1, + Stencil = 1 << 2, - public bool HasColor; - public bool HasDepth; - public bool HasStencil; + DepthStencil = Depth | Stencil + } - public bool Compressed; + private struct ImageDescriptor + { + public int BytesPerPixel { get; private set; } + public int BlockWidth { get; private set; } + public int BlockHeight { get; private set; } - public ImageDescriptor( - TextureReaderDelegate Reader, - bool HasColor, - bool HasDepth, - bool HasStencil, - bool Compressed) + public TargetBuffer Target { get; private set; } + + public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, TargetBuffer Target) { - this.Reader = Reader; - this.HasColor = HasColor; - this.HasDepth = HasDepth; - this.HasStencil = HasStencil; - this.Compressed = Compressed; + this.BytesPerPixel = BytesPerPixel; + this.BlockWidth = BlockWidth; + this.BlockHeight = BlockHeight; + this.Target = Target; } } - private const GalImageFormat Snorm = GalImageFormat.Snorm; - private const GalImageFormat Unorm = GalImageFormat.Unorm; - private const GalImageFormat Sint = GalImageFormat.Sint; - private const GalImageFormat Uint = GalImageFormat.Uint; - private const GalImageFormat Sfloat = GalImageFormat.Sfloat; + private const GalImageFormat Snorm = GalImageFormat.Snorm; + private const GalImageFormat Unorm = GalImageFormat.Unorm; + private const GalImageFormat Sint = GalImageFormat.Sint; + private const GalImageFormat Uint = GalImageFormat.Uint; + private const GalImageFormat Float = GalImageFormat.Float; + private const GalImageFormat Srgb = GalImageFormat.Srgb; private static readonly Dictionary s_TextureTable = new Dictionary() - { - { GalTextureFormat.R32G32B32A32, GalImageFormat.R32G32B32A32 | Sint | Uint | Sfloat }, - { GalTextureFormat.R16G16B16A16, GalImageFormat.R16G16B16A16 | Snorm | Unorm | Sint | Uint | Sfloat }, - { GalTextureFormat.R32G32, GalImageFormat.R32G32 | Sint | Uint | Sfloat }, - { GalTextureFormat.A8B8G8R8, GalImageFormat.A8B8G8R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.A2B10G10R10, GalImageFormat.A2B10G10R10 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.G8R8, GalImageFormat.G8R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Sfloat }, - { GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Sfloat }, - { GalTextureFormat.A4B4G4R4, GalImageFormat.A4B4G4R4 | Unorm }, - { GalTextureFormat.A1B5G5R5, GalImageFormat.A1R5G5B5 | Unorm }, - { GalTextureFormat.B5G6R5, GalImageFormat.B5G6R5 | Unorm }, - { GalTextureFormat.BF10GF11RF11, GalImageFormat.B10G11R11 | Sfloat }, - { GalTextureFormat.Z24S8, GalImageFormat.D24_S8 | Unorm }, - { GalTextureFormat.ZF32, GalImageFormat.D32 | Sfloat }, - { GalTextureFormat.ZF32_X24S8, GalImageFormat.D32_S8 | Unorm }, + { + { GalTextureFormat.RGBA32, GalImageFormat.RGBA32 | Sint | Uint | Float }, + { GalTextureFormat.RGBA16, GalImageFormat.RGBA16 | Snorm | Unorm | Sint | Uint | Float }, + { GalTextureFormat.RG32, GalImageFormat.RG32 | Sint | Uint | Float }, + { GalTextureFormat.RGBA8, GalImageFormat.RGBA8 | Snorm | Unorm | Sint | Uint | Srgb }, + { GalTextureFormat.RGB10A2, GalImageFormat.RGB10A2 | Snorm | Unorm | Sint | Uint }, + { GalTextureFormat.RG8, GalImageFormat.RG8 | Snorm | Unorm | Sint | Uint }, + { GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Float }, + { GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint }, + { GalTextureFormat.RG16, GalImageFormat.RG16 | Snorm | Unorm | Float }, + { GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Float }, + { GalTextureFormat.RGBA4, GalImageFormat.RGBA4 | Unorm }, + { GalTextureFormat.RGB5A1, GalImageFormat.RGB5A1 | Unorm }, + { GalTextureFormat.RGB565, GalImageFormat.RGB565 | Unorm }, + { GalTextureFormat.R11G11B10F, GalImageFormat.R11G11B10 | Float }, + { GalTextureFormat.D24S8, GalImageFormat.D24S8 | Unorm | Uint }, + { GalTextureFormat.D32F, GalImageFormat.D32 | Float }, + { GalTextureFormat.D32FX24S8, GalImageFormat.D32S8 | Float }, + { GalTextureFormat.D16, GalImageFormat.D16 | Unorm }, - //Compressed formats - { GalTextureFormat.BC6H_SF16, GalImageFormat.BC6H_SF16 | Unorm }, - { GalTextureFormat.BC6H_UF16, GalImageFormat.BC6H_UF16 | Unorm }, - { GalTextureFormat.BC7U, GalImageFormat.BC7 | Unorm }, - { GalTextureFormat.BC1, GalImageFormat.BC1_RGBA | Unorm }, - { GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm }, - { GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm }, - { GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm }, - { GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm }, - { GalTextureFormat.Astc2D4x4, GalImageFormat.ASTC_4x4 | Unorm }, - { GalTextureFormat.Astc2D5x5, GalImageFormat.ASTC_5x5 | Unorm }, - { GalTextureFormat.Astc2D6x6, GalImageFormat.ASTC_6x6 | Unorm }, - { GalTextureFormat.Astc2D8x8, GalImageFormat.ASTC_8x8 | Unorm }, - { GalTextureFormat.Astc2D10x10, GalImageFormat.ASTC_10x10 | Unorm }, - { GalTextureFormat.Astc2D12x12, GalImageFormat.ASTC_12x12 | Unorm }, - { GalTextureFormat.Astc2D5x4, GalImageFormat.ASTC_5x4 | Unorm }, - { GalTextureFormat.Astc2D6x5, GalImageFormat.ASTC_6x5 | Unorm }, - { GalTextureFormat.Astc2D8x6, GalImageFormat.ASTC_8x6 | Unorm }, - { GalTextureFormat.Astc2D10x8, GalImageFormat.ASTC_10x8 | Unorm }, - { GalTextureFormat.Astc2D12x10, GalImageFormat.ASTC_12x10 | Unorm }, - { GalTextureFormat.Astc2D8x5, GalImageFormat.ASTC_8x5 | Unorm }, - { GalTextureFormat.Astc2D10x5, GalImageFormat.ASTC_10x5 | Unorm }, - { GalTextureFormat.Astc2D10x6, GalImageFormat.ASTC_10x6 | Unorm } - }; + //Compressed formats + { GalTextureFormat.BptcSfloat, GalImageFormat.BptcSfloat | Float }, + { GalTextureFormat.BptcUfloat, GalImageFormat.BptcUfloat | Float }, + { GalTextureFormat.BptcUnorm, GalImageFormat.BptcUnorm | Unorm | Srgb }, + { GalTextureFormat.BC1, GalImageFormat.BC1 | Unorm | Srgb }, + { GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm | Srgb }, + { GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm | Srgb }, + { GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm }, + { GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm }, + { GalTextureFormat.Astc2D4x4, GalImageFormat.Astc2D4x4 | Unorm | Srgb }, + { GalTextureFormat.Astc2D5x5, GalImageFormat.Astc2D5x5 | Unorm | Srgb }, + { GalTextureFormat.Astc2D6x6, GalImageFormat.Astc2D6x6 | Unorm | Srgb }, + { GalTextureFormat.Astc2D8x8, GalImageFormat.Astc2D8x8 | Unorm | Srgb }, + { GalTextureFormat.Astc2D10x10, GalImageFormat.Astc2D10x10 | Unorm | Srgb }, + { GalTextureFormat.Astc2D12x12, GalImageFormat.Astc2D12x12 | Unorm | Srgb }, + { GalTextureFormat.Astc2D5x4, GalImageFormat.Astc2D5x4 | Unorm | Srgb }, + { GalTextureFormat.Astc2D6x5, GalImageFormat.Astc2D6x5 | Unorm | Srgb }, + { GalTextureFormat.Astc2D8x6, GalImageFormat.Astc2D8x6 | Unorm | Srgb }, + { GalTextureFormat.Astc2D10x8, GalImageFormat.Astc2D10x8 | Unorm | Srgb }, + { GalTextureFormat.Astc2D12x10, GalImageFormat.Astc2D12x10 | Unorm | Srgb }, + { GalTextureFormat.Astc2D8x5, GalImageFormat.Astc2D8x5 | Unorm | Srgb }, + { GalTextureFormat.Astc2D10x5, GalImageFormat.Astc2D10x5 | Unorm | Srgb }, + { GalTextureFormat.Astc2D10x6, GalImageFormat.Astc2D10x6 | Unorm | Srgb } + }; private static readonly Dictionary s_ImageTable = new Dictionary() - { - { GalImageFormat.R32G32B32A32, new ImageDescriptor(TextureReader.Read16Bpp, true, false, false, false) }, - { GalImageFormat.R16G16B16A16, new ImageDescriptor(TextureReader.Read8Bpp, true, false, false, false) }, - { GalImageFormat.R32G32, new ImageDescriptor(TextureReader.Read8Bpp, true, false, false, false) }, - { GalImageFormat.A8B8G8R8, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) }, - { GalImageFormat.A2B10G10R10, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) }, - { GalImageFormat.R32, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) }, - { GalImageFormat.A4B4G4R4, new ImageDescriptor(TextureReader.Read2Bpp, true, false, false, false) }, - { GalImageFormat.BC6H_SF16, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) }, - { GalImageFormat.BC6H_UF16, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) }, - { GalImageFormat.A1R5G5B5, new ImageDescriptor(TextureReader.Read5551, true, false, false, false) }, - { GalImageFormat.B5G6R5, new ImageDescriptor(TextureReader.Read565, true, false, false, false) }, - { GalImageFormat.BC7, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) }, - { GalImageFormat.R16G16, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) }, - { GalImageFormat.R8G8, new ImageDescriptor(TextureReader.Read2Bpp, true, false, false, false) }, - { GalImageFormat.G8R8, new ImageDescriptor(TextureReader.Read2Bpp, true, false, false, false) }, - { GalImageFormat.R16, new ImageDescriptor(TextureReader.Read2Bpp, true, false, false, false) }, - { GalImageFormat.R8, new ImageDescriptor(TextureReader.Read1Bpp, true, false, false, false) }, - { GalImageFormat.B10G11R11, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) }, - { GalImageFormat.A8B8G8R8_SRGB, new ImageDescriptor(TextureReader.Read4Bpp, true, false, false, false) }, - { GalImageFormat.BC1_RGBA, new ImageDescriptor(TextureReader.Read8Bpt4x4, true, false, false, true) }, - { GalImageFormat.BC2, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) }, - { GalImageFormat.BC3, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) }, - { GalImageFormat.BC4, new ImageDescriptor(TextureReader.Read8Bpt4x4, true, false, false, true) }, - { GalImageFormat.BC5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) }, - { GalImageFormat.ASTC_4x4, new ImageDescriptor(TextureReader.Read16BptCompressedTexture4x4, true, false, false, true) }, - { GalImageFormat.ASTC_5x5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture5x5, true, false, false, true) }, - { GalImageFormat.ASTC_6x6, new ImageDescriptor(TextureReader.Read16BptCompressedTexture6x6, true, false, false, true) }, - { GalImageFormat.ASTC_8x8, new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x8, true, false, false, true) }, - { GalImageFormat.ASTC_10x10, new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x10, true, false, false, true) }, - { GalImageFormat.ASTC_12x12, new ImageDescriptor(TextureReader.Read16BptCompressedTexture12x12, true, false, false, true) }, - { GalImageFormat.ASTC_5x4, new ImageDescriptor(TextureReader.Read16BptCompressedTexture5x4, true, false, false, true) }, - { GalImageFormat.ASTC_6x5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture6x5, true, false, false, true) }, - { GalImageFormat.ASTC_8x6, new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x6, true, false, false, true) }, - { GalImageFormat.ASTC_10x8, new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x8, true, false, false, true) }, - { GalImageFormat.ASTC_12x10, new ImageDescriptor(TextureReader.Read16BptCompressedTexture12x10, true, false, false, true) }, - { GalImageFormat.ASTC_8x5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture8x5, true, false, false, true) }, - { GalImageFormat.ASTC_10x5, new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x5, true, false, false, true) }, - { GalImageFormat.ASTC_10x6, new ImageDescriptor(TextureReader.Read16BptCompressedTexture10x6, true, false, false, true) }, + { + { GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBX8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BGR5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) }, + { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) }, + { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) }, + { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) }, + { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) }, + { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) }, - { GalImageFormat.D24_S8, new ImageDescriptor(TextureReader.Read4Bpp, false, true, true, false) }, - { GalImageFormat.D32, new ImageDescriptor(TextureReader.Read4Bpp, false, true, false, false) }, - { GalImageFormat.D16, new ImageDescriptor(TextureReader.Read2Bpp, false, true, false, false) }, - { GalImageFormat.D32_S8, new ImageDescriptor(TextureReader.Read8Bpp, false, true, true, false) }, - }; + { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D24, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) }, + { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) } + }; public static GalImageFormat ConvertTexture( GalTextureFormat Format, - GalTextureType RType, - GalTextureType GType, - GalTextureType BType, - GalTextureType AType) + GalTextureType RType, + GalTextureType GType, + GalTextureType BType, + GalTextureType AType, + bool ConvSrgb) { if (RType != GType || RType != BType || RType != AType) { - throw new NotImplementedException("Per component types are not implemented"); + throw new NotImplementedException("Per component types are not implemented!"); } if (!s_TextureTable.TryGetValue(Format, out GalImageFormat ImageFormat)) { - throw new NotImplementedException("Texture with format " + ((int)Format).ToString("x2") + " not implemented"); + throw new NotImplementedException($"Format 0x{((int)Format):x} not implemented!"); } - GalTextureType Type = RType; + GalImageFormat FormatType = ConvSrgb ? Srgb : GetFormatType(RType); - GalImageFormat FormatType = GetFormatType(RType); + GalImageFormat CombinedFormat = (ImageFormat & GalImageFormat.FormatMask) | FormatType; - if (ImageFormat.HasFlag(FormatType)) + if (!ImageFormat.HasFlag(FormatType)) { - return (ImageFormat & GalImageFormat.FormatMask) | FormatType; - } - else - { - throw new NotImplementedException("Texture with format " + Format + - " and component type " + Type + " is not implemented"); + throw new NotImplementedException($"Format \"{CombinedFormat}\" not implemented!"); } + + return CombinedFormat; } public static GalImageFormat ConvertSurface(GalSurfaceFormat Format) { switch (Format) { - case GalSurfaceFormat.RGBA32Float: return GalImageFormat.R32G32B32A32 | Sfloat; - case GalSurfaceFormat.RGBA16Float: return GalImageFormat.R16G16B16A16 | Sfloat; - case GalSurfaceFormat.RG32Float: return GalImageFormat.R32G32 | Sfloat; - case GalSurfaceFormat.RG32Sint: return GalImageFormat.R32G32 | Sint; - case GalSurfaceFormat.RG32Uint: return GalImageFormat.R32G32 | Uint; - case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.R8G8B8A8 | Unorm; //Is this right? - case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.A8B8G8R8_SRGB; //This one might be wrong - case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.A2B10G10R10 | Unorm; - case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.A8B8G8R8 | Unorm; - case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.A8B8G8R8_SRGB; - case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.A8B8G8R8 | Snorm; - case GalSurfaceFormat.RG16Snorm: return GalImageFormat.R16G16 | Snorm; - case GalSurfaceFormat.RG16Float: return GalImageFormat.R16G16 | Sfloat; - case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.B10G11R11 | Sfloat; - case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Sfloat; - case GalSurfaceFormat.RG8Unorm: return GalImageFormat.R8G8 | Unorm; - case GalSurfaceFormat.RG8Snorm: return GalImageFormat.R8 | Snorm; - case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Sfloat; - case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm; + case GalSurfaceFormat.RGBA32Float: return GalImageFormat.RGBA32 | Float; + case GalSurfaceFormat.RGBA32Uint: return GalImageFormat.RGBA32 | Uint; + case GalSurfaceFormat.RGBA16Float: return GalImageFormat.RGBA16 | Float; + case GalSurfaceFormat.RG32Float: return GalImageFormat.RG32 | Float; + case GalSurfaceFormat.RG32Sint: return GalImageFormat.RG32 | Sint; + case GalSurfaceFormat.RG32Uint: return GalImageFormat.RG32 | Uint; + case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.BGRA8 | Unorm; + case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.BGRA8 | Srgb; + case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.RGB10A2 | Unorm; + case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.RGBA8 | Unorm; + case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.RGBA8 | Srgb; + case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.RGBA8 | Snorm; + case GalSurfaceFormat.RG16Snorm: return GalImageFormat.RG16 | Snorm; + case GalSurfaceFormat.RG16Unorm: return GalImageFormat.RG16 | Unorm; + case GalSurfaceFormat.RG16Float: return GalImageFormat.RG16 | Float; + case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.R11G11B10 | Float; + case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Float; + case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint; + case GalSurfaceFormat.RG8Unorm: return GalImageFormat.RG8 | Unorm; + case GalSurfaceFormat.RG8Snorm: return GalImageFormat.RG8 | Snorm; + case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Float; + case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm; + case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint; + case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm; + case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint; + case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.RGB565 | Unorm; + case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.BGR5A1 | Unorm; + case GalSurfaceFormat.RGBX8Unorm: return GalImageFormat.RGBX8 | Unorm; } throw new NotImplementedException(Format.ToString()); @@ -194,142 +210,221 @@ namespace Ryujinx.Graphics.Texture { switch (Format) { - case GalZetaFormat.Z32Float: return GalImageFormat.D32 | Sfloat; - case GalZetaFormat.S8Z24Unorm: return GalImageFormat.D24_S8 | Unorm; - case GalZetaFormat.Z16Unorm: return GalImageFormat.D16 | Unorm; - //This one might not be Uint, change when a texture uses this format - case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_S8 | Uint; + case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float; + case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm; + case GalZetaFormat.D16Unorm: return GalImageFormat.D16 | Unorm; + case GalZetaFormat.D24X8Unorm: return GalImageFormat.D24 | Unorm; + case GalZetaFormat.D24S8Unorm: return GalImageFormat.D24S8 | Unorm; + case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float; } throw new NotImplementedException(Format.ToString()); } - public static TextureReaderDelegate GetReader(GalImageFormat Format) + public static byte[] ReadTexture(IMemory Memory, GalImage Image, long Position) { - return GetImageDescriptor(Format).Reader; + MemoryManager CpuMemory; + + if (Memory is NvGpuVmm Vmm) + { + CpuMemory = Vmm.Memory; + } + else + { + CpuMemory = (MemoryManager)Memory; + } + + ISwizzle Swizzle = TextureHelper.GetSwizzle(Image); + + ImageDescriptor Desc = GetImageDescriptor(Image.Format); + + (int Width, int Height) = GetImageSizeInBlocks(Image); + + int BytesPerPixel = Desc.BytesPerPixel; + + //Note: Each row of the texture needs to be aligned to 4 bytes. + int Pitch = (Width * BytesPerPixel + 3) & ~3; + + byte[] Data = new byte[Height * Pitch]; + + for (int Y = 0; Y < Height; Y++) + { + int OutOffs = Y * Pitch; + + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel); + + OutOffs += BytesPerPixel; + } + } + + return Data; + } + + public static void WriteTexture(NvGpuVmm Vmm, GalImage Image, long Position, byte[] Data) + { + ISwizzle Swizzle = TextureHelper.GetSwizzle(Image); + + ImageDescriptor Desc = GetImageDescriptor(Image.Format); + + (int Width, int Height) = ImageUtils.GetImageSizeInBlocks(Image); + + int BytesPerPixel = Desc.BytesPerPixel; + + int InOffs = 0; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + + Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel); + + InOffs += BytesPerPixel; + } + } + + public static bool CopyTexture( + NvGpuVmm Vmm, + GalImage SrcImage, + GalImage DstImage, + long SrcAddress, + long DstAddress, + int SrcX, + int SrcY, + int DstX, + int DstY, + int Width, + int Height) + { + ISwizzle SrcSwizzle = TextureHelper.GetSwizzle(SrcImage); + ISwizzle DstSwizzle = TextureHelper.GetSwizzle(DstImage); + + ImageDescriptor Desc = GetImageDescriptor(SrcImage.Format); + + if (GetImageDescriptor(DstImage.Format).BytesPerPixel != Desc.BytesPerPixel) + { + return false; + } + + int BytesPerPixel = Desc.BytesPerPixel; + + for (int Y = 0; Y < Height; Y++) + for (int X = 0; X < Width; X++) + { + long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y); + long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y); + + byte[] Texel = Vmm.ReadBytes(SrcAddress + SrcOffset, BytesPerPixel); + + Vmm.WriteBytes(DstAddress + DstOffset, Texel); + } + + return true; } public static int GetSize(GalImage Image) { - switch (Image.Format & GalImageFormat.FormatMask) + ImageDescriptor Desc = GetImageDescriptor(Image.Format); + + int Width = DivRoundUp(Image.Width, Desc.BlockWidth); + int Height = DivRoundUp(Image.Height, Desc.BlockHeight); + + return Desc.BytesPerPixel * Width * Height; + } + + public static int GetPitch(GalImageFormat Format, int Width) + { + ImageDescriptor Desc = GetImageDescriptor(Format); + + int Pitch = Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth); + + Pitch = (Pitch + 0x1f) & ~0x1f; + + return Pitch; + } + + public static int GetBlockWidth(GalImageFormat Format) + { + return GetImageDescriptor(Format).BlockWidth; + } + + public static int GetBlockHeight(GalImageFormat Format) + { + return GetImageDescriptor(Format).BlockHeight; + } + + public static int GetAlignedWidth(GalImage Image) + { + ImageDescriptor Desc = GetImageDescriptor(Image.Format); + + int AlignMask; + + if (Image.Layout == GalMemoryLayout.BlockLinear) { - case GalImageFormat.R32G32B32A32: - return Image.Width * Image.Height * 16; - - case GalImageFormat.R16G16B16A16: - case GalImageFormat.D32_S8: - case GalImageFormat.R32G32: - return Image.Width * Image.Height * 8; - - case GalImageFormat.A8B8G8R8: - case GalImageFormat.A8B8G8R8_SRGB: - case GalImageFormat.A2B10G10R10: - case GalImageFormat.R16G16: - case GalImageFormat.R32: - case GalImageFormat.D32: - case GalImageFormat.B10G11R11: - case GalImageFormat.D24_S8: - return Image.Width * Image.Height * 4; - - case GalImageFormat.B4G4R4A4: - case GalImageFormat.A1R5G5B5: - case GalImageFormat.B5G6R5: - case GalImageFormat.R8G8: - case GalImageFormat.R16: - case GalImageFormat.D16: - return Image.Width * Image.Height * 2; - - case GalImageFormat.R8: - return Image.Width * Image.Height; - - case GalImageFormat.BC1_RGBA: - case GalImageFormat.BC4: - { - return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 8); - } - - case GalImageFormat.BC6H_SF16: - case GalImageFormat.BC6H_UF16: - case GalImageFormat.BC7: - case GalImageFormat.BC2: - case GalImageFormat.BC3: - case GalImageFormat.BC5: - case GalImageFormat.ASTC_4x4: - return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 16); - - case GalImageFormat.ASTC_5x5: - return CompressedTextureSize(Image.Width, Image.Height, 5, 5, 16); - - case GalImageFormat.ASTC_6x6: - return CompressedTextureSize(Image.Width, Image.Height, 6, 6, 16); - - case GalImageFormat.ASTC_8x8: - return CompressedTextureSize(Image.Width, Image.Height, 8, 8, 16); - - case GalImageFormat.ASTC_10x10: - return CompressedTextureSize(Image.Width, Image.Height, 10, 10, 16); - - case GalImageFormat.ASTC_12x12: - return CompressedTextureSize(Image.Width, Image.Height, 12, 12, 16); - - case GalImageFormat.ASTC_5x4: - return CompressedTextureSize(Image.Width, Image.Height, 5, 4, 16); - - case GalImageFormat.ASTC_6x5: - return CompressedTextureSize(Image.Width, Image.Height, 6, 5, 16); - - case GalImageFormat.ASTC_8x6: - return CompressedTextureSize(Image.Width, Image.Height, 8, 6, 16); - - case GalImageFormat.ASTC_10x8: - return CompressedTextureSize(Image.Width, Image.Height, 10, 8, 16); - - case GalImageFormat.ASTC_12x10: - return CompressedTextureSize(Image.Width, Image.Height, 12, 10, 16); - - case GalImageFormat.ASTC_8x5: - return CompressedTextureSize(Image.Width, Image.Height, 8, 5, 16); - - case GalImageFormat.ASTC_10x5: - return CompressedTextureSize(Image.Width, Image.Height, 10, 5, 16); - - case GalImageFormat.ASTC_10x6: - return CompressedTextureSize(Image.Width, Image.Height, 10, 6, 16); + AlignMask = Image.TileWidth * (64 / Desc.BytesPerPixel) - 1; + } + else + { + AlignMask = (32 / Desc.BytesPerPixel) - 1; } - throw new NotImplementedException((Image.Format & GalImageFormat.FormatMask).ToString()); + return (Image.Width + AlignMask) & ~AlignMask; + } + + public static (int Width, int Height) GetImageSizeInBlocks(GalImage Image) + { + ImageDescriptor Desc = GetImageDescriptor(Image.Format); + + return (DivRoundUp(Image.Width, Desc.BlockWidth), + DivRoundUp(Image.Height, Desc.BlockHeight)); + } + + public static int GetBytesPerPixel(GalImageFormat Format) + { + return GetImageDescriptor(Format).BytesPerPixel; + } + + private static int DivRoundUp(int LHS, int RHS) + { + return (LHS + (RHS - 1)) / RHS; } public static bool HasColor(GalImageFormat Format) { - return GetImageDescriptor(Format).HasColor; + return (GetImageDescriptor(Format).Target & TargetBuffer.Color) != 0; } public static bool HasDepth(GalImageFormat Format) { - return GetImageDescriptor(Format).HasDepth; + return (GetImageDescriptor(Format).Target & TargetBuffer.Depth) != 0; } public static bool HasStencil(GalImageFormat Format) { - return GetImageDescriptor(Format).HasStencil; + return (GetImageDescriptor(Format).Target & TargetBuffer.Stencil) != 0; } public static bool IsCompressed(GalImageFormat Format) { - return GetImageDescriptor(Format).Compressed; + ImageDescriptor Desc = GetImageDescriptor(Format); + + return (Desc.BlockWidth | Desc.BlockHeight) != 1; } private static ImageDescriptor GetImageDescriptor(GalImageFormat Format) { - GalImageFormat TypeLess = (Format & GalImageFormat.FormatMask); + GalImageFormat PixelFormat = Format & GalImageFormat.FormatMask; - if (s_ImageTable.TryGetValue(TypeLess, out ImageDescriptor Descriptor)) + if (s_ImageTable.TryGetValue(PixelFormat, out ImageDescriptor Descriptor)) { return Descriptor; } - throw new NotImplementedException("Image with format " + TypeLess.ToString() + " not implemented"); + throw new NotImplementedException($"Format \"{PixelFormat}\" not implemented!"); } private static GalImageFormat GetFormatType(GalTextureType Type) @@ -340,18 +435,10 @@ namespace Ryujinx.Graphics.Texture case GalTextureType.Unorm: return Unorm; case GalTextureType.Sint: return Sint; case GalTextureType.Uint: return Uint; - case GalTextureType.Float: return Sfloat; + case GalTextureType.Float: return Float; default: throw new NotImplementedException(((int)Type).ToString()); } } - - private static int CompressedTextureSize(int TextureWidth, int TextureHeight, int BlockWidth, int BlockHeight, int Bpb) - { - int W = (TextureWidth + (BlockWidth - 1)) / BlockWidth; - int H = (TextureHeight + (BlockHeight - 1)) / BlockHeight; - - return W * H * Bpb; - } } -} \ No newline at end of file +} diff --git a/Ryujinx.Graphics/Texture/TextureFactory.cs b/Ryujinx.Graphics/Texture/TextureFactory.cs index fa7a0f80e0..1f2d625ec4 100644 --- a/Ryujinx.Graphics/Texture/TextureFactory.cs +++ b/Ryujinx.Graphics/Texture/TextureFactory.cs @@ -17,44 +17,20 @@ namespace Ryujinx.Graphics.Texture GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7); GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7); - int Width = (Tic[4] & 0xffff) + 1; - int Height = (Tic[5] & 0xffff) + 1; - - return new GalImage( - Width, - Height, - Format, - XSource, - YSource, - ZSource, - WSource); - } - - public static byte[] GetTextureData(NvGpuVmm Vmm, long TicPosition) - { - int[] Tic = ReadWords(Vmm, TicPosition, 8); - - GalImageFormat Format = GetImageFormat(Tic); - - long TextureAddress = (uint)Tic[1]; - - TextureAddress |= (long)((ushort)Tic[2]) << 32; - TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7); + GalMemoryLayout Layout; + if (Swizzle == TextureSwizzle.BlockLinear || Swizzle == TextureSwizzle.BlockLinearColorKey) { - TextureAddress &= ~0x1ffL; + Layout = GalMemoryLayout.BlockLinear; } - else if (Swizzle == TextureSwizzle.Pitch || - Swizzle == TextureSwizzle.PitchColorKey) + else { - TextureAddress &= ~0x1fL; + Layout = GalMemoryLayout.Pitch; } - int Pitch = (Tic[3] & 0xffff) << 5; - int BlockHeightLog2 = (Tic[3] >> 3) & 7; int TileWidthLog2 = (Tic[3] >> 10) & 7; @@ -64,17 +40,24 @@ namespace Ryujinx.Graphics.Texture int Width = (Tic[4] & 0xffff) + 1; int Height = (Tic[5] & 0xffff) + 1; - TextureInfo Texture = new TextureInfo( - TextureAddress, + GalImage Image = new GalImage( Width, Height, - Pitch, - BlockHeight, TileWidth, - Swizzle, - Format); + BlockHeight, + Layout, + Format, + XSource, + YSource, + ZSource, + WSource); - return TextureReader.Read(Vmm, Texture); + if (Layout == GalMemoryLayout.Pitch) + { + Image.Pitch = (Tic[3] & 0xffff) << 5; + } + + return Image; } public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition) @@ -107,14 +90,16 @@ namespace Ryujinx.Graphics.Texture private static GalImageFormat GetImageFormat(int[] Tic) { - GalTextureType RType = (GalTextureType)((Tic[0] >> 7) & 7); + GalTextureType RType = (GalTextureType)((Tic[0] >> 7) & 7); GalTextureType GType = (GalTextureType)((Tic[0] >> 10) & 7); GalTextureType BType = (GalTextureType)((Tic[0] >> 13) & 7); GalTextureType AType = (GalTextureType)((Tic[0] >> 16) & 7); GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); - return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType); + bool ConvSrgb = ((Tic[4] >> 22) & 1) != 0; + + return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType, ConvSrgb); } private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count) diff --git a/Ryujinx.Graphics/Texture/TextureHelper.cs b/Ryujinx.Graphics/Texture/TextureHelper.cs index 8130ab41ae..6ac91d8b59 100644 --- a/Ryujinx.Graphics/Texture/TextureHelper.cs +++ b/Ryujinx.Graphics/Texture/TextureHelper.cs @@ -1,45 +1,42 @@ using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; -using System; namespace Ryujinx.Graphics.Texture { static class TextureHelper { - public static ISwizzle GetSwizzle(TextureInfo Texture, int BlockWidth, int Bpp) + public static ISwizzle GetSwizzle(GalImage Image) { - int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth; + int BlockWidth = ImageUtils.GetBlockWidth (Image.Format); + int BytesPerPixel = ImageUtils.GetBytesPerPixel(Image.Format); - int AlignMask = Texture.TileWidth * (64 / Bpp) - 1; + int Width = (Image.Width + (BlockWidth - 1)) / BlockWidth; - Width = (Width + AlignMask) & ~AlignMask; - - switch (Texture.Swizzle) + if (Image.Layout == GalMemoryLayout.BlockLinear) { - case TextureSwizzle._1dBuffer: - case TextureSwizzle.Pitch: - case TextureSwizzle.PitchColorKey: - return new LinearSwizzle(Texture.Pitch, Bpp); + int AlignMask = Image.TileWidth * (64 / BytesPerPixel) - 1; - case TextureSwizzle.BlockLinear: - case TextureSwizzle.BlockLinearColorKey: - return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight); + Width = (Width + AlignMask) & ~AlignMask; + + return new BlockLinearSwizzle(Width, BytesPerPixel, Image.GobBlockHeight); + } + else + { + return new LinearSwizzle(Image.Pitch, BytesPerPixel); } - - throw new NotImplementedException(Texture.Swizzle.ToString()); } - public static (AMemory Memory, long Position) GetMemoryAndPosition( - IAMemory Memory, - long Position) + public static (MemoryManager Memory, long Position) GetMemoryAndPosition( + IMemory Memory, + long Position) { if (Memory is NvGpuVmm Vmm) { return (Vmm.Memory, Vmm.GetPhysicalAddress(Position)); } - return ((AMemory)Memory, Position); + return ((MemoryManager)Memory, Position); } } } diff --git a/Ryujinx.Graphics/Texture/TextureInfo.cs b/Ryujinx.Graphics/Texture/TextureInfo.cs deleted file mode 100644 index 66445dcce7..0000000000 --- a/Ryujinx.Graphics/Texture/TextureInfo.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Ryujinx.Graphics.Gal; - -namespace Ryujinx.Graphics.Texture -{ - public struct TextureInfo - { - public long Position { get; private set; } - - public int Width { get; private set; } - public int Height { get; private set; } - public int Pitch { get; private set; } - - public int BlockHeight { get; private set; } - public int TileWidth { get; private set; } - - public TextureSwizzle Swizzle { get; private set; } - - public GalImageFormat Format { get; private set; } - - public TextureInfo( - long Position, - int Width, - int Height) - { - this.Position = Position; - this.Width = Width; - this.Height = Height; - - Pitch = 0; - - BlockHeight = 16; - - TileWidth = 1; - - Swizzle = TextureSwizzle.BlockLinear; - - Format = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; - } - - public TextureInfo( - long Position, - int Width, - int Height, - int Pitch, - int BlockHeight, - int TileWidth, - TextureSwizzle Swizzle, - GalImageFormat Format) - { - this.Position = Position; - this.Width = Width; - this.Height = Height; - this.Pitch = Pitch; - this.BlockHeight = BlockHeight; - this.TileWidth = TileWidth; - this.Swizzle = Swizzle; - this.Format = Format; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Texture/TextureReader.cs b/Ryujinx.Graphics/Texture/TextureReader.cs deleted file mode 100644 index dbaed1a8e8..0000000000 --- a/Ryujinx.Graphics/Texture/TextureReader.cs +++ /dev/null @@ -1,398 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Graphics.Gal; -using System; - -namespace Ryujinx.Graphics.Texture -{ - delegate byte[] TextureReaderDelegate(IAMemory Memory, TextureInfo Texture); - - public static class TextureReader - { - public static byte[] Read(IAMemory Memory, TextureInfo Texture) - { - TextureReaderDelegate Reader = ImageUtils.GetReader(Texture.Format); - - return Reader(Memory, Texture); - } - - internal unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 1); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - byte Pixel = CpuMem.ReadByte(Position + Offset); - - *(BuffPtr + OutOffs) = Pixel; - - OutOffs++; - } - } - - return Output; - } - - internal unsafe static byte[] Read5551(IAMemory Memory, TextureInfo Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 2]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset); - - Pixel = (Pixel & 0x001f) << 11 | - (Pixel & 0x03e0) << 1 | - (Pixel & 0x7c00) >> 9 | - (Pixel & 0x8000) >> 15; - - *(short*)(BuffPtr + OutOffs) = (short)Pixel; - - OutOffs += 2; - } - } - - return Output; - } - - internal unsafe static byte[] Read565(IAMemory Memory, TextureInfo Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 2]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset); - - Pixel = (Pixel & 0x001f) << 11 | - (Pixel & 0x07e0) | - (Pixel & 0xf800) >> 11; - - *(short*)(BuffPtr + OutOffs) = (short)Pixel; - - OutOffs += 2; - } - } - - return Output; - } - - internal unsafe static byte[] Read2Bpp(IAMemory Memory, TextureInfo Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 2]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - short Pixel = CpuMem.ReadInt16(Position + Offset); - - *(short*)(BuffPtr + OutOffs) = Pixel; - - OutOffs += 2; - } - } - - return Output; - } - - internal unsafe static byte[] Read4Bpp(IAMemory Memory, TextureInfo Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 4]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 4); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - int Pixel = CpuMem.ReadInt32(Position + Offset); - - *(int*)(BuffPtr + OutOffs) = Pixel; - - OutOffs += 4; - } - } - - return Output; - } - - internal unsafe static byte[] Read8Bpp(IAMemory Memory, TextureInfo Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 8]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 8); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - long Pixel = CpuMem.ReadInt64(Position + Offset); - - *(long*)(BuffPtr + OutOffs) = Pixel; - - OutOffs += 8; - } - } - - return Output; - } - - internal unsafe static byte[] Read16Bpp(IAMemory Memory, TextureInfo Texture) - { - int Width = Texture.Width; - int Height = Texture.Height; - - byte[] Output = new byte[Width * Height * 16]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 16); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - long PxLow = CpuMem.ReadInt64(Position + Offset + 0); - long PxHigh = CpuMem.ReadInt64(Position + Offset + 8); - - *(long*)(BuffPtr + OutOffs + 0) = PxLow; - *(long*)(BuffPtr + OutOffs + 8) = PxHigh; - - OutOffs += 16; - } - } - - return Output; - } - - internal unsafe static byte[] Read8Bpt4x4(IAMemory Memory, TextureInfo Texture) - { - int Width = (Texture.Width + 3) / 4; - int Height = (Texture.Height + 3) / 4; - - byte[] Output = new byte[Width * Height * 8]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 4, 8); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - long Tile = CpuMem.ReadInt64(Position + Offset); - - *(long*)(BuffPtr + OutOffs) = Tile; - - OutOffs += 8; - } - } - - return Output; - } - - internal unsafe static byte[] Read16BptCompressedTexture(IAMemory Memory, TextureInfo Texture, int BlockWidth, int BlockHeight) - { - int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth; - int Height = (Texture.Height + (BlockHeight - 1)) / BlockHeight; - - byte[] Output = new byte[Width * Height * 16]; - - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, BlockWidth, 16); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Output) - { - long OutOffs = 0; - - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - long Tile0 = CpuMem.ReadInt64(Position + Offset + 0); - long Tile1 = CpuMem.ReadInt64(Position + Offset + 8); - - *(long*)(BuffPtr + OutOffs + 0) = Tile0; - *(long*)(BuffPtr + OutOffs + 8) = Tile1; - - OutOffs += 16; - } - } - - return Output; - } - - internal static byte[] Read16BptCompressedTexture4x4(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 4, 4); - } - - internal static byte[] Read16BptCompressedTexture5x5(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 5, 5); - } - - internal static byte[] Read16BptCompressedTexture6x6(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 6, 6); - } - - internal static byte[] Read16BptCompressedTexture8x8(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 8, 8); - } - - internal static byte[] Read16BptCompressedTexture10x10(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 10, 10); - } - - internal static byte[] Read16BptCompressedTexture12x12(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 12, 12); - } - - internal static byte[] Read16BptCompressedTexture5x4(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 5, 4); - } - - internal static byte[] Read16BptCompressedTexture6x5(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 6, 5); - } - - internal static byte[] Read16BptCompressedTexture8x6(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 8, 6); - } - - internal static byte[] Read16BptCompressedTexture10x8(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 10, 8); - } - - internal static byte[] Read16BptCompressedTexture12x10(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 12, 10); - } - - internal static byte[] Read16BptCompressedTexture8x5(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 5, 5); - } - - internal static byte[] Read16BptCompressedTexture10x5(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 10, 5); - } - - internal static byte[] Read16BptCompressedTexture10x6(IAMemory Memory, TextureInfo Texture) - { - return Read16BptCompressedTexture(Memory, Texture, 10, 6); - } - } -} diff --git a/Ryujinx.Graphics/Texture/TextureWriter.cs b/Ryujinx.Graphics/Texture/TextureWriter.cs deleted file mode 100644 index 16e78c56f3..0000000000 --- a/Ryujinx.Graphics/Texture/TextureWriter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; - -namespace Ryujinx.Graphics.Texture -{ - static class TextureWriter - { - public unsafe static void Write(IAMemory Memory, TextureInfo Texture, byte[] Data) - { - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 4); - - (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( - Memory, - Texture.Position); - - fixed (byte* BuffPtr = Data) - { - long InOffs = 0; - - for (int Y = 0; Y < Texture.Height; Y++) - for (int X = 0; X < Texture.Width; X++) - { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - - int Pixel = *(int*)(BuffPtr + InOffs); - - CpuMem.WriteInt32(Position + Offset, Pixel); - - InOffs += 4; - } - } - } - } -} diff --git a/Ryujinx.Graphics/ValueRange.cs b/Ryujinx.Graphics/ValueRange.cs new file mode 100644 index 0000000000..6298bd8ed8 --- /dev/null +++ b/Ryujinx.Graphics/ValueRange.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics +{ + struct ValueRange + { + public long Start { get; private set; } + public long End { get; private set; } + + public T Value { get; set; } + + public ValueRange(long Start, long End, T Value = default(T)) + { + this.Start = Start; + this.End = End; + this.Value = Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/ValueRangeSet.cs b/Ryujinx.Graphics/ValueRangeSet.cs new file mode 100644 index 0000000000..42125bcece --- /dev/null +++ b/Ryujinx.Graphics/ValueRangeSet.cs @@ -0,0 +1,234 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics +{ + class ValueRangeSet + { + private List> Ranges; + + public ValueRangeSet() + { + Ranges = new List>(); + } + + public void Add(ValueRange Range) + { + if (Range.End <= Range.Start) + { + //Empty or invalid range, do nothing. + return; + } + + int First = BinarySearchFirstIntersection(Range); + + if (First == -1) + { + //No intersections case. + //Find first greater than range (after the current one). + //If found, add before, otherwise add to the end of the list. + int GtIndex = BinarySearchGt(Range); + + if (GtIndex != -1) + { + Ranges.Insert(GtIndex, Range); + } + else + { + Ranges.Add(Range); + } + + return; + } + + (int Start, int End) = GetAllIntersectionRanges(Range, First); + + ValueRange Prev = Ranges[Start]; + ValueRange Next = Ranges[End]; + + Ranges.RemoveRange(Start, (End - Start) + 1); + + InsertNextNeighbour(Start, Range, Next); + + int NewIndex = Start; + + Ranges.Insert(Start, Range); + + InsertPrevNeighbour(Start, Range, Prev); + + //Try merging neighbours if the value is equal. + if (NewIndex > 0) + { + Prev = Ranges[NewIndex - 1]; + + if (Prev.End == Range.Start && CompareValues(Prev, Range)) + { + Ranges.RemoveAt(--NewIndex); + + Ranges[NewIndex] = new ValueRange(Prev.Start, Range.End, Range.Value); + } + } + + if (NewIndex < Ranges.Count - 1) + { + Next = Ranges[NewIndex + 1]; + + if (Next.Start == Range.End && CompareValues(Next, Range)) + { + Ranges.RemoveAt(NewIndex + 1); + + Ranges[NewIndex] = new ValueRange(Ranges[NewIndex].Start, Next.End, Range.Value); + } + } + } + + private bool CompareValues(ValueRange LHS, ValueRange RHS) + { + return LHS.Value?.Equals(RHS.Value) ?? RHS.Value == null; + } + + public void Remove(ValueRange Range) + { + int First = BinarySearchFirstIntersection(Range); + + if (First == -1) + { + //Nothing to remove. + return; + } + + (int Start, int End) = GetAllIntersectionRanges(Range, First); + + ValueRange Prev = Ranges[Start]; + ValueRange Next = Ranges[End]; + + Ranges.RemoveRange(Start, (End - Start) + 1); + + InsertNextNeighbour(Start, Range, Next); + InsertPrevNeighbour(Start, Range, Prev); + } + + private void InsertNextNeighbour(int Index, ValueRange Range, ValueRange Next) + { + //Split last intersection (ordered by Start) if necessary. + if (Range.End < Next.End) + { + InsertNewRange(Index, Range.End, Next.End, Next.Value); + } + } + + private void InsertPrevNeighbour(int Index, ValueRange Range, ValueRange Prev) + { + //Split first intersection (ordered by Start) if necessary. + if (Range.Start > Prev.Start) + { + InsertNewRange(Index, Prev.Start, Range.Start, Prev.Value); + } + } + + private void InsertNewRange(int Index, long Start, long End, T Value) + { + Ranges.Insert(Index, new ValueRange(Start, End, Value)); + } + + public ValueRange[] GetAllIntersections(ValueRange Range) + { + int First = BinarySearchFirstIntersection(Range); + + if (First == -1) + { + return new ValueRange[0]; + } + + (int Start, int End) = GetAllIntersectionRanges(Range, First); + + return Ranges.GetRange(Start, (End - Start) + 1).ToArray(); + } + + private (int Start, int End) GetAllIntersectionRanges(ValueRange Range, int BaseIndex) + { + int Start = BaseIndex; + int End = BaseIndex; + + while (Start > 0 && Intersects(Range, Ranges[Start - 1])) + { + Start--; + } + + while (End < Ranges.Count - 1 && Intersects(Range, Ranges[End + 1])) + { + End++; + } + + return (Start, End); + } + + private int BinarySearchFirstIntersection(ValueRange Range) + { + int Left = 0; + int Right = Ranges.Count - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + ValueRange Current = Ranges[Middle]; + + if (Intersects(Range, Current)) + { + return Middle; + } + + if (Range.Start < Current.Start) + { + Right = Middle - 1; + } + else + { + Left = Middle + 1; + } + } + + return -1; + } + + private int BinarySearchGt(ValueRange Range) + { + int GtIndex = -1; + + int Left = 0; + int Right = Ranges.Count - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + ValueRange Current = Ranges[Middle]; + + if (Range.Start < Current.Start) + { + Right = Middle - 1; + + if (GtIndex == -1 || Current.Start < Ranges[GtIndex].Start) + { + GtIndex = Middle; + } + } + else + { + Left = Middle + 1; + } + } + + return GtIndex; + } + + private bool Intersects(ValueRange LHS, ValueRange RHS) + { + return LHS.Start < RHS.End && RHS.Start < LHS.End; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/FileSystem/SaveHelper.cs b/Ryujinx.HLE/FileSystem/SaveHelper.cs index 67f010169c..b74d853c79 100644 --- a/Ryujinx.HLE/FileSystem/SaveHelper.cs +++ b/Ryujinx.HLE/FileSystem/SaveHelper.cs @@ -35,9 +35,11 @@ namespace Ryujinx.HLE.FileSystem } } + string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString(); + string SavePath = Path.Combine(BaseSavePath, SaveMetaData.SaveId.ToString("x16"), - SaveMetaData.UserId.ToString(), + SaveAccount, SaveMetaData.SaveDataType == SaveDataType.SaveData ? CurrentTitleId.ToString("x16") : string.Empty); return SavePath; diff --git a/Ryujinx.HLE/FileSystem/SaveInfo.cs b/Ryujinx.HLE/FileSystem/SaveInfo.cs index f3790ec787..3acd33fdae 100644 --- a/Ryujinx.HLE/FileSystem/SaveInfo.cs +++ b/Ryujinx.HLE/FileSystem/SaveInfo.cs @@ -1,4 +1,4 @@ -using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.Utilities; namespace Ryujinx.HLE.FileSystem { @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.FileSystem { public long TitleId { get; private set; } public long SaveId { get; private set; } - public UserId UserId { get; private set; } + public UInt128 UserId { get; private set; } public SaveDataType SaveDataType { get; private set; } public SaveSpaceId SaveSpaceId { get; private set; } @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.FileSystem long TitleId, long SaveId, SaveDataType SaveDataType, - UserId UserId, + UInt128 UserId, SaveSpaceId SaveSpaceId) { this.TitleId = TitleId; diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs deleted file mode 100644 index aea979c26c..0000000000 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler.cs +++ /dev/null @@ -1,416 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Ryujinx.HLE.HOS.Diagnostics -{ - static class Demangler - { - private static readonly Dictionary BuiltinTypes = new Dictionary - { - { "v", "void" }, - { "w", "wchar_t" }, - { "b", "bool" }, - { "c", "char" }, - { "a", "signed char" }, - { "h", "unsigned char" }, - { "s", "short" }, - { "t", "unsigned short" }, - { "i", "int" }, - { "j", "unsigned int" }, - { "l", "long" }, - { "m", "unsigned long" }, - { "x", "long long" }, - { "y", "unsigned long long" }, - { "n", "__int128" }, - { "o", "unsigned __int128" }, - { "f", "float" }, - { "d", "double" }, - { "e", "long double" }, - { "g", "__float128" }, - { "z", "..." }, - { "Dd", "__iec559_double" }, - { "De", "__iec559_float128" }, - { "Df", "__iec559_float" }, - { "Dh", "__iec559_float16" }, - { "Di", "char32_t" }, - { "Ds", "char16_t" }, - { "Da", "decltype(auto)" }, - { "Dn", "std::nullptr_t" }, - }; - - private static readonly Dictionary SubstitutionExtra = new Dictionary - { - {"Sa", "std::allocator"}, - {"Sb", "std::basic_string"}, - {"Ss", "std::basic_string, ::std::allocator>"}, - {"Si", "std::basic_istream>"}, - {"So", "std::basic_ostream>"}, - {"Sd", "std::basic_iostream>"} - }; - - private static int FromBase36(string encoded) - { - string base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; - char[] reversedEncoded = encoded.ToLower().ToCharArray().Reverse().ToArray(); - int result = 0; - for (int i = 0; i < reversedEncoded.Length; i++) - { - char c = reversedEncoded[i]; - int value = base36.IndexOf(c); - if (value == -1) - return -1; - result += value * (int)Math.Pow(36, i); - } - return result; - } - - private static string GetCompressedValue(string compression, List compressionData, out int pos) - { - string res = null; - bool canHaveUnqualifiedName = false; - pos = -1; - if (compressionData.Count == 0 || !compression.StartsWith("S")) - return null; - - if (compression.Length >= 2 && SubstitutionExtra.TryGetValue(compression.Substring(0, 2), out string substitutionValue)) - { - pos = 1; - res = substitutionValue; - compression = compression.Substring(2); - } - else if (compression.StartsWith("St")) - { - pos = 1; - canHaveUnqualifiedName = true; - res = "std"; - compression = compression.Substring(2); - } - else if (compression.StartsWith("S_")) - { - pos = 1; - res = compressionData[0]; - canHaveUnqualifiedName = true; - compression = compression.Substring(2); - } - else - { - int id = -1; - int underscorePos = compression.IndexOf('_'); - if (underscorePos == -1) - return null; - string partialId = compression.Substring(1, underscorePos - 1); - - id = FromBase36(partialId); - if (id == -1 || compressionData.Count <= (id + 1)) - { - return null; - } - res = compressionData[id + 1]; - pos = partialId.Length + 1; - canHaveUnqualifiedName= true; - compression = compression.Substring(pos); - } - if (res != null) - { - if (canHaveUnqualifiedName) - { - List type = ReadName(compression, compressionData, out int endOfNameType); - if (endOfNameType != -1 && type != null) - { - pos += endOfNameType; - res = res + "::" + type[type.Count - 1]; - } - } - } - return res; - } - - private static List ReadName(string mangled, List compressionData, out int pos, bool isNested = true) - { - List res = new List(); - string charCountString = null; - int charCount = 0; - int i; - - pos = -1; - for (i = 0; i < mangled.Length; i++) - { - char chr = mangled[i]; - if (charCountString == null) - { - if (ReadCVQualifiers(chr) != null) - { - continue; - } - if (chr == 'S') - { - string data = GetCompressedValue(mangled.Substring(i), compressionData, out pos); - if (pos == -1) - { - return null; - } - if (res.Count == 0) - res.Add(data); - else - res.Add(res[res.Count - 1] + "::" + data); - i += pos; - if (i < mangled.Length && mangled[i] == 'E') - { - break; - } - continue; - } - else if (chr == 'E') - { - break; - } - } - if (Char.IsDigit(chr)) - { - charCountString += chr; - } - else - { - if (!int.TryParse(charCountString, out charCount)) - { - return null; - } - string demangledPart = mangled.Substring(i, charCount); - if (res.Count == 0) - res.Add(demangledPart); - else - res.Add(res[res.Count - 1] + "::" + demangledPart); - i = i + charCount - 1; - charCount = 0; - charCountString = null; - if (!isNested) - break; - } - } - if (res.Count == 0) - { - return null; - } - pos = i; - return res; - } - - private static string ReadBuiltinType(string mangledType, out int pos) - { - string res = null; - string possibleBuiltinType; - pos = -1; - possibleBuiltinType = mangledType[0].ToString(); - if (!BuiltinTypes.TryGetValue(possibleBuiltinType, out res)) - { - if (mangledType.Length >= 2) - { - // Try to match the first 2 chars if the first call failed - possibleBuiltinType = mangledType.Substring(0, 2); - BuiltinTypes.TryGetValue(possibleBuiltinType, out res); - } - } - if (res != null) - pos = possibleBuiltinType.Length; - return res; - } - - private static string ReadCVQualifiers(char qualifier) - { - if (qualifier == 'r') - return "restricted"; - else if (qualifier == 'V') - return "volatile"; - else if (qualifier == 'K') - return "const"; - return null; - } - - private static string ReadRefQualifiers(char qualifier) - { - if (qualifier == 'R') - return "&"; - else if (qualifier == 'O') - return "&&"; - return null; - } - - private static string ReadSpecialQualifiers(char qualifier) - { - if (qualifier == 'P') - return "*"; - else if (qualifier == 'C') - return "complex"; - else if (qualifier == 'G') - return "imaginary"; - return null; - } - - private static List ReadParameters(string mangledParams, List compressionData, out int pos) - { - List res = new List(); - List refQualifiers = new List(); - string parsedTypePart = null; - string currentRefQualifiers = null; - string currentBuiltinType = null; - string currentSpecialQualifiers = null; - string currentCompressedValue = null; - int i = 0; - pos = -1; - - for (i = 0; i < mangledParams.Length; i++) - { - if (currentBuiltinType != null) - { - string currentCVQualifier = String.Join(" ", refQualifiers); - // Try to mimic the compression indexing - if (currentRefQualifiers != null) - { - compressionData.Add(currentBuiltinType + currentRefQualifiers); - } - if (refQualifiers.Count != 0) - { - compressionData.Add(currentBuiltinType + " " + currentCVQualifier + currentRefQualifiers); - } - if (currentSpecialQualifiers != null) - { - compressionData.Add(currentBuiltinType + " " + currentCVQualifier + currentRefQualifiers + currentSpecialQualifiers); - } - if (currentRefQualifiers == null && currentCVQualifier == null && currentSpecialQualifiers == null) - { - compressionData.Add(currentBuiltinType); - } - currentBuiltinType = null; - currentCompressedValue = null; - currentCVQualifier = null; - currentRefQualifiers = null; - refQualifiers.Clear(); - currentSpecialQualifiers = null; - } - char chr = mangledParams[i]; - string part = mangledParams.Substring(i); - - // Try to read qualifiers - parsedTypePart = ReadCVQualifiers(chr); - if (parsedTypePart != null) - { - refQualifiers.Add(parsedTypePart); - - // need more data - continue; - } - - parsedTypePart = ReadRefQualifiers(chr); - if (parsedTypePart != null) - { - currentRefQualifiers = parsedTypePart; - - // need more data - continue; - } - - parsedTypePart = ReadSpecialQualifiers(chr); - if (parsedTypePart != null) - { - currentSpecialQualifiers = parsedTypePart; - - // need more data - continue; - } - - // TODO: extended-qualifier? - - if (part.StartsWith("S")) - { - parsedTypePart = GetCompressedValue(part, compressionData, out pos); - if (pos != -1 && parsedTypePart != null) - { - currentCompressedValue = parsedTypePart; - i += pos; - res.Add(currentCompressedValue + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); - currentBuiltinType = null; - currentCompressedValue = null; - currentRefQualifiers = null; - refQualifiers.Clear(); - currentSpecialQualifiers = null; - continue; - } - pos = -1; - return null; - } - else if (part.StartsWith("N")) - { - part = part.Substring(1); - List name = ReadName(part, compressionData, out pos); - if (pos != -1 && name != null) - { - i += pos + 1; - res.Add(name[name.Count - 1] + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); - currentBuiltinType = null; - currentCompressedValue = null; - currentRefQualifiers = null; - refQualifiers.Clear(); - currentSpecialQualifiers = null; - continue; - } - } - - // Try builting - parsedTypePart = ReadBuiltinType(part, out pos); - if (pos == -1) - { - return null; - } - currentBuiltinType = parsedTypePart; - res.Add(currentBuiltinType + " " + String.Join(" ", refQualifiers) + currentRefQualifiers + currentSpecialQualifiers); - i = i + pos -1; - } - pos = i; - return res; - } - - private static string ParseFunctionName(string mangled) - { - List compressionData = new List(); - int pos = 0; - string res; - bool isNested = mangled.StartsWith("N"); - - // If it's start with "N" it must be a nested function name - if (isNested) - mangled = mangled.Substring(1); - compressionData = ReadName(mangled, compressionData, out pos, isNested); - if (pos == -1) - return null; - res = compressionData[compressionData.Count - 1]; - compressionData.Remove(res); - mangled = mangled.Substring(pos + 1); - - // more data? maybe not a data name so... - if (mangled != String.Empty) - { - List parameters = ReadParameters(mangled, compressionData, out pos); - // parameters parsing error, we return the original data to avoid information loss. - if (pos == -1) - return null; - parameters = parameters.Select(outer => outer.Trim()).ToList(); - res += "(" + String.Join(", ", parameters) + ")"; - } - return res; - } - - public static string Parse(string originalMangled) - { - if (originalMangled.StartsWith("_Z")) - { - // We assume that we have a name (TOOD: support special names) - string res = ParseFunctionName(originalMangled.Substring(2)); - if (res == null) - return originalMangled; - return res; - } - return originalMangled; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs new file mode 100644 index 0000000000..435789e0f2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArraySubscriptingExpression.cs @@ -0,0 +1,25 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ArraySubscriptingExpression : BaseNode + { + private BaseNode LeftNode; + private BaseNode Subscript; + + public ArraySubscriptingExpression(BaseNode LeftNode, BaseNode Subscript) : base(NodeType.ArraySubscriptingExpression) + { + this.LeftNode = LeftNode; + this.Subscript = Subscript; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + LeftNode.Print(Writer); + Writer.Write(")["); + Subscript.Print(Writer); + Writer.Write("]"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs new file mode 100644 index 0000000000..1679736072 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ArrayType.cs @@ -0,0 +1,59 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ArrayType : BaseNode + { + private BaseNode Base; + private BaseNode DimensionExpression; + private string DimensionString; + + public ArrayType(BaseNode Base, BaseNode DimensionExpression = null) : base(NodeType.ArrayType) + { + this.Base = Base; + this.DimensionExpression = DimensionExpression; + } + + public ArrayType(BaseNode Base, string DimensionString) : base(NodeType.ArrayType) + { + this.Base = Base; + this.DimensionString = DimensionString; + } + + public override bool HasRightPart() + { + return true; + } + + public override bool IsArray() + { + return true; + } + + public override void PrintLeft(TextWriter Writer) + { + Base.PrintLeft(Writer); + } + + public override void PrintRight(TextWriter Writer) + { + // FIXME: detect if previous char was a ]. + Writer.Write(" "); + + Writer.Write("["); + + if (DimensionString != null) + { + Writer.Write(DimensionString); + } + else if (DimensionExpression != null) + { + DimensionExpression.Print(Writer); + } + + Writer.Write("]"); + + Base.PrintRight(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs new file mode 100644 index 0000000000..870758462e --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BaseNode.cs @@ -0,0 +1,113 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public enum NodeType + { + CVQualifierType, + SimpleReferenceType, + NameType, + EncodedFunction, + NestedName, + SpecialName, + LiteralOperator, + NodeArray, + ElaboratedType, + PostfixQualifiedType, + SpecialSubstitution, + ExpandedSpecialSubstitution, + CtorDtorNameType, + EnclosedExpression, + ForwardTemplateReference, + NameTypeWithTemplateArguments, + PackedTemplateArgument, + TemplateArguments, + BooleanExpression, + CastExpression, + CallExpression, + IntegerCastExpression, + PackedTemplateParameter, + PackedTemplateParameterExpansion, + IntegerLiteral, + DeleteExpression, + MemberExpression, + ArraySubscriptingExpression, + InitListExpression, + PostfixExpression, + ConditionalExpression, + ThrowExpression, + FunctionParameter, + ConversionExpression, + BinaryExpression, + PrefixExpression, + BracedExpression, + BracedRangeExpression, + NewExpression, + QualifiedName, + StdQualifiedName, + DtOrName, + GlobalQualifiedName, + NoexceptSpec, + DynamicExceptionSpec, + FunctionType, + PointerType, + ReferenceType, + ConversionOperatorType, + LocalName, + CtorVtableSpecialName, + ArrayType + } + + public abstract class BaseNode + { + public NodeType Type { get; protected set; } + + public BaseNode(NodeType Type) + { + this.Type = Type; + } + + public virtual void Print(TextWriter Writer) + { + PrintLeft(Writer); + + if (HasRightPart()) + { + PrintRight(Writer); + } + } + + public abstract void PrintLeft(TextWriter Writer); + + public virtual bool HasRightPart() + { + return false; + } + + public virtual bool IsArray() + { + return false; + } + + public virtual bool HasFunctions() + { + return false; + } + + public virtual string GetName() + { + return null; + } + + public virtual void PrintRight(TextWriter Writer) {} + + public override string ToString() + { + StringWriter Writer = new StringWriter(); + + Print(Writer); + + return Writer.ToString(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs new file mode 100644 index 0000000000..9cd1dd779a --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BinaryExpression.cs @@ -0,0 +1,41 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class BinaryExpression : BaseNode + { + private BaseNode LeftPart; + private string Name; + private BaseNode RightPart; + + public BinaryExpression(BaseNode LeftPart, string Name, BaseNode RightPart) : base(NodeType.BinaryExpression) + { + this.LeftPart = LeftPart; + this.Name = Name; + this.RightPart = RightPart; + } + + public override void PrintLeft(TextWriter Writer) + { + if (Name.Equals(">")) + { + Writer.Write("("); + } + + Writer.Write("("); + LeftPart.Print(Writer); + Writer.Write(") "); + + Writer.Write(Name); + + Writer.Write(" ("); + RightPart.Print(Writer); + Writer.Write(")"); + + if (Name.Equals(">")) + { + Writer.Write(")"); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs new file mode 100644 index 0000000000..59222ea3f9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedExpression.cs @@ -0,0 +1,40 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class BracedExpression : BaseNode + { + private BaseNode Element; + private BaseNode Expression; + private bool IsArrayExpression; + + public BracedExpression(BaseNode Element, BaseNode Expression, bool IsArrayExpression) : base(NodeType.BracedExpression) + { + this.Element = Element; + this.Expression = Expression; + this.IsArrayExpression = IsArrayExpression; + } + + public override void PrintLeft(TextWriter Writer) + { + if (IsArrayExpression) + { + Writer.Write("["); + Element.Print(Writer); + Writer.Write("]"); + } + else + { + Writer.Write("."); + Element.Print(Writer); + } + + if (!Expression.GetType().Equals(NodeType.BracedExpression) || !Expression.GetType().Equals(NodeType.BracedRangeExpression)) + { + Writer.Write(" = "); + } + + Expression.Print(Writer); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs new file mode 100644 index 0000000000..e459f1a393 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/BracedRangeExpression.cs @@ -0,0 +1,34 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class BracedRangeExpression : BaseNode + { + private BaseNode FirstNode; + private BaseNode LastNode; + private BaseNode Expression; + + public BracedRangeExpression(BaseNode FirstNode, BaseNode LastNode, BaseNode Expression) : base(NodeType.BracedRangeExpression) + { + this.FirstNode = FirstNode; + this.LastNode = LastNode; + this.Expression = Expression; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("["); + FirstNode.Print(Writer); + Writer.Write(" ... "); + LastNode.Print(Writer); + Writer.Write("]"); + + if (!Expression.GetType().Equals(NodeType.BracedExpression) || !Expression.GetType().Equals(NodeType.BracedRangeExpression)) + { + Writer.Write(" = "); + } + + Expression.Print(Writer); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs new file mode 100644 index 0000000000..ae43fcdb21 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class CallExpression : NodeArray + { + private BaseNode Callee; + + public CallExpression(BaseNode Callee, List Nodes) : base(Nodes, NodeType.CallExpression) + { + this.Callee = Callee; + } + + public override void PrintLeft(TextWriter Writer) + { + Callee.Print(Writer); + + Writer.Write("("); + Writer.Write(string.Join(", ", Nodes.ToArray())); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs new file mode 100644 index 0000000000..c02e9e6555 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs @@ -0,0 +1,28 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class CastExpression : BaseNode + { + private string Kind; + private BaseNode To; + private BaseNode From; + + public CastExpression(string Kind, BaseNode To, BaseNode From) : base(NodeType.CastExpression) + { + this.Kind = Kind; + this.To = To; + this.From = From; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(Kind); + Writer.Write("<"); + To.PrintLeft(Writer); + Writer.Write(">("); + From.PrintLeft(Writer); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs new file mode 100644 index 0000000000..17ac7c1a20 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConditionalExpression.cs @@ -0,0 +1,29 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ConditionalExpression : BaseNode + { + private BaseNode ThenNode; + private BaseNode ElseNode; + private BaseNode ConditionNode; + + public ConditionalExpression(BaseNode ConditionNode, BaseNode ThenNode, BaseNode ElseNode) : base(NodeType.ConditionalExpression) + { + this.ThenNode = ThenNode; + this.ConditionNode = ConditionNode; + this.ElseNode = ElseNode; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + ConditionNode.Print(Writer); + Writer.Write(") ? ("); + ThenNode.Print(Writer); + Writer.Write(") : ("); + ElseNode.Print(Writer); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs new file mode 100644 index 0000000000..7c5d35d810 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionExpression.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ConversionExpression : BaseNode + { + private BaseNode TypeNode; + private BaseNode Expressions; + + public ConversionExpression(BaseNode TypeNode, BaseNode Expressions) : base(NodeType.ConversionExpression) + { + this.TypeNode = TypeNode; + this.Expressions = Expressions; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + TypeNode.Print(Writer); + Writer.Write(")("); + Expressions.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs new file mode 100644 index 0000000000..55d4eecab9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ConversionOperatorType.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ConversionOperatorType : ParentNode + { + public ConversionOperatorType(BaseNode Child) : base(NodeType.ConversionOperatorType, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("operator "); + Child.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs new file mode 100644 index 0000000000..49ed386d08 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorDtorNameType.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class CtorDtorNameType : ParentNode + { + private bool IsDestructor; + + public CtorDtorNameType(BaseNode Name, bool IsDestructor) : base(NodeType.CtorDtorNameType, Name) + { + this.IsDestructor = IsDestructor; + } + + public override void PrintLeft(TextWriter Writer) + { + if (IsDestructor) + { + Writer.Write("~"); + } + + Writer.Write(Child.GetName()); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs new file mode 100644 index 0000000000..7630dbe570 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CtorVtableSpecialName.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class CtorVtableSpecialName : BaseNode + { + private BaseNode FirstType; + private BaseNode SecondType; + + public CtorVtableSpecialName(BaseNode FirstType, BaseNode SecondType) : base(NodeType.CtorVtableSpecialName) + { + this.FirstType = FirstType; + this.SecondType = SecondType; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("construction vtable for "); + FirstType.Print(Writer); + Writer.Write("-in-"); + SecondType.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs new file mode 100644 index 0000000000..22c34c4219 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DeleteExpression.cs @@ -0,0 +1,33 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class DeleteExpression : ParentNode + { + private bool IsGlobal; + private bool IsArrayExpression; + + public DeleteExpression(BaseNode Child, bool IsGlobal, bool IsArrayExpression) : base(NodeType.DeleteExpression, Child) + { + this.IsGlobal = IsGlobal; + this.IsArrayExpression = IsArrayExpression; + } + + public override void PrintLeft(TextWriter Writer) + { + if (IsGlobal) + { + Writer.Write("::"); + } + + Writer.Write("delete"); + + if (IsArrayExpression) + { + Writer.Write("[] "); + } + + Child.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs new file mode 100644 index 0000000000..c65c4cfb3a --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DtorName.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class DtorName : ParentNode + { + public DtorName(BaseNode Name) : base(NodeType.DtOrName, Name) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("~"); + Child.PrintLeft(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs new file mode 100644 index 0000000000..dca5f0dfd0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/DynamicExceptionSpec.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class DynamicExceptionSpec : ParentNode + { + public DynamicExceptionSpec(BaseNode Child) : base(NodeType.DynamicExceptionSpec, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("throw("); + Child.Print(Writer); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs new file mode 100644 index 0000000000..11f89c8d33 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ElaboratedType.cs @@ -0,0 +1,21 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ElaboratedType : ParentNode + { + private string Elaborated; + + public ElaboratedType(string Elaborated, BaseNode Type) : base(NodeType.ElaboratedType, Type) + { + this.Elaborated = Elaborated; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(Elaborated); + Writer.Write(" "); + Child.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs new file mode 100644 index 0000000000..dc991aa09c --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EnclosedExpression.cs @@ -0,0 +1,25 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class EnclosedExpression : BaseNode + { + private string Prefix; + private BaseNode Expression; + private string Postfix; + + public EnclosedExpression(string Prefix, BaseNode Expression, string Postfix) : base(NodeType.EnclosedExpression) + { + this.Prefix = Prefix; + this.Expression = Expression; + this.Postfix = Postfix; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(Prefix); + Expression.Print(Writer); + Writer.Write(Postfix); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs new file mode 100644 index 0000000000..37a9a7afe0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/EncodedFunction.cs @@ -0,0 +1,77 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class EncodedFunction : BaseNode + { + private BaseNode Name; + private BaseNode Params; + private BaseNode CV; + private BaseNode Ref; + private BaseNode Attrs; + private BaseNode Ret; + + public EncodedFunction(BaseNode Name, BaseNode Params, BaseNode CV, BaseNode Ref, BaseNode Attrs, BaseNode Ret) : base(NodeType.NameType) + { + this.Name = Name; + this.Params = Params; + this.CV = CV; + this.Ref = Ref; + this.Attrs = Attrs; + this.Ret = Ret; + } + + public override void PrintLeft(TextWriter Writer) + { + if (Ret != null) + { + Ret.PrintLeft(Writer); + + if (!Ret.HasRightPart()) + { + Writer.Write(" "); + } + } + + Name.Print(Writer); + + } + + public override bool HasRightPart() + { + return true; + } + + public override void PrintRight(TextWriter Writer) + { + Writer.Write("("); + + if (Params != null) + { + Params.Print(Writer); + } + + Writer.Write(")"); + + if (Ret != null) + { + Ret.PrintRight(Writer); + } + + if (CV != null) + { + CV.Print(Writer); + } + + if (Ref != null) + { + Ref.Print(Writer); + } + + if (Attrs != null) + { + Attrs.Print(Writer); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs new file mode 100644 index 0000000000..e015299846 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FoldExpression.cs @@ -0,0 +1,48 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class FoldExpression : BaseNode + { + private bool IsLeftFold; + private string OperatorName; + private BaseNode Expression; + private BaseNode Initializer; + + public FoldExpression(bool IsLeftFold, string OperatorName, BaseNode Expression, BaseNode Initializer) : base(NodeType.FunctionParameter) + { + this.IsLeftFold = IsLeftFold; + this.OperatorName = OperatorName; + this.Expression = Expression; + this.Initializer = Initializer; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + + if (IsLeftFold && Initializer != null) + { + Initializer.Print(Writer); + Writer.Write(" "); + Writer.Write(OperatorName); + Writer.Write(" "); + } + + Writer.Write(IsLeftFold ? "... " : " "); + Writer.Write(OperatorName); + Writer.Write(!IsLeftFold ? " ..." : " "); + Expression.Print(Writer); + + if (!IsLeftFold && Initializer != null) + { + Initializer.Print(Writer); + Writer.Write(" "); + Writer.Write(OperatorName); + Writer.Write(" "); + } + + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs new file mode 100644 index 0000000000..6456e47bfa --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ForwardTemplateReference.cs @@ -0,0 +1,36 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ForwardTemplateReference : BaseNode + { + // TODO: Compute inside the Demangler + public BaseNode Reference; + private int Index; + + public ForwardTemplateReference(int Index) : base(NodeType.ForwardTemplateReference) + { + this.Index = Index; + } + + public override string GetName() + { + return Reference.GetName(); + } + + public override void PrintLeft(TextWriter Writer) + { + Reference.PrintLeft(Writer); + } + + public override void PrintRight(TextWriter Writer) + { + Reference.PrintRight(Writer); + } + + public override bool HasRightPart() + { + return Reference.HasRightPart(); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs new file mode 100644 index 0000000000..5a1ca61d55 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionParameter.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class FunctionParameter : BaseNode + { + private string Number; + + public FunctionParameter(string Number) : base(NodeType.FunctionParameter) + { + this.Number = Number; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("fp "); + + if (Number != null) + { + Writer.Write(Number); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs new file mode 100644 index 0000000000..c727eab9c5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/FunctionType.cs @@ -0,0 +1,61 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class FunctionType : BaseNode + { + private BaseNode ReturnType; + private BaseNode Params; + private BaseNode CVQualifier; + private SimpleReferenceType ReferenceQualifier; + private BaseNode ExceptionSpec; + + public FunctionType(BaseNode ReturnType, BaseNode Params, BaseNode CVQualifier, SimpleReferenceType ReferenceQualifier, BaseNode ExceptionSpec) : base(NodeType.FunctionType) + { + this.ReturnType = ReturnType; + this.Params = Params; + this.CVQualifier = CVQualifier; + this.ReferenceQualifier = ReferenceQualifier; + this.ExceptionSpec = ExceptionSpec; + } + + public override void PrintLeft(TextWriter Writer) + { + ReturnType.PrintLeft(Writer); + Writer.Write(" "); + } + + public override void PrintRight(TextWriter Writer) + { + Writer.Write("("); + Params.Print(Writer); + Writer.Write(")"); + + ReturnType.PrintRight(Writer); + + CVQualifier.Print(Writer); + + if (ReferenceQualifier.Qualifier != Reference.None) + { + Writer.Write(" "); + ReferenceQualifier.PrintQualifier(Writer); + } + + if (ExceptionSpec != null) + { + Writer.Write(" "); + ExceptionSpec.Print(Writer); + } + } + + public override bool HasRightPart() + { + return true; + } + + public override bool HasFunctions() + { + return true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs new file mode 100644 index 0000000000..2346c1bf70 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/GlobalQualifiedName.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class GlobalQualifiedName : ParentNode + { + public GlobalQualifiedName(BaseNode Child) : base(NodeType.GlobalQualifiedName, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("::"); + Child.Print(Writer); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs new file mode 100644 index 0000000000..2ed4daa46e --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class InitListExpression : BaseNode + { + private BaseNode TypeNode; + private List Nodes; + + public InitListExpression(BaseNode TypeNode, List Nodes) : base(NodeType.InitListExpression) + { + this.TypeNode = TypeNode; + this.Nodes = Nodes; + } + + public override void PrintLeft(TextWriter Writer) + { + if (TypeNode != null) + { + TypeNode.Print(Writer); + } + + Writer.Write("{"); + Writer.Write(string.Join(", ", Nodes.ToArray())); + Writer.Write("}"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs new file mode 100644 index 0000000000..984c9aefb0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerCastExpression.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class IntegerCastExpression : ParentNode + { + private string Number; + + public IntegerCastExpression(BaseNode Type, string Number) : base(NodeType.IntegerCastExpression, Type) + { + this.Number = Number; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + Child.Print(Writer); + Writer.Write(")"); + Writer.Write(Number); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs new file mode 100644 index 0000000000..215cf6dce5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/IntegerLiteral.cs @@ -0,0 +1,41 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class IntegerLiteral : BaseNode + { + private string LitteralName; + private string LitteralValue; + + public IntegerLiteral(string LitteralName, string LitteralValue) : base(NodeType.IntegerLiteral) + { + this.LitteralValue = LitteralValue; + this.LitteralName = LitteralName; + } + + public override void PrintLeft(TextWriter Writer) + { + if (LitteralName.Length > 3) + { + Writer.Write("("); + Writer.Write(LitteralName); + Writer.Write(")"); + } + + if (LitteralValue[0] == 'n') + { + Writer.Write("-"); + Writer.Write(LitteralValue.Substring(1)); + } + else + { + Writer.Write(LitteralValue); + } + + if (LitteralName.Length <= 3) + { + Writer.Write(LitteralName); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs new file mode 100644 index 0000000000..f9bd4a6ef5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LiteralOperator.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class LiteralOperator : ParentNode + { + public LiteralOperator(BaseNode Child) : base(NodeType.LiteralOperator, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("operator \""); + Child.PrintLeft(Writer); + Writer.Write("\""); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs new file mode 100644 index 0000000000..44c216289c --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/LocalName.cs @@ -0,0 +1,23 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class LocalName : BaseNode + { + private BaseNode Encoding; + private BaseNode Entity; + + public LocalName(BaseNode Encoding, BaseNode Entity) : base(NodeType.LocalName) + { + this.Encoding = Encoding; + this.Entity = Entity; + } + + public override void PrintLeft(TextWriter Writer) + { + Encoding.Print(Writer); + Writer.Write("::"); + Entity.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs new file mode 100644 index 0000000000..dd3d02dbd1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/MemberExpression.cs @@ -0,0 +1,25 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class MemberExpression : BaseNode + { + private BaseNode LeftNode; + private string Kind; + private BaseNode RightNode; + + public MemberExpression(BaseNode LeftNode, string Kind, BaseNode RightNode) : base(NodeType.MemberExpression) + { + this.LeftNode = LeftNode; + this.Kind = Kind; + this.RightNode = RightNode; + } + + public override void PrintLeft(TextWriter Writer) + { + LeftNode.Print(Writer); + Writer.Write(Kind); + RightNode.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs new file mode 100644 index 0000000000..029440cb77 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameType.cs @@ -0,0 +1,29 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NameType : BaseNode + { + private string NameValue; + + public NameType(string NameValue, NodeType Type) : base(Type) + { + this.NameValue = NameValue; + } + + public NameType(string NameValue) : base(NodeType.NameType) + { + this.NameValue = NameValue; + } + + public override string GetName() + { + return NameValue; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(NameValue); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs new file mode 100644 index 0000000000..e16bd1508b --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NameTypeWithTemplateArguments.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NameTypeWithTemplateArguments : BaseNode + { + private BaseNode Prev; + private BaseNode TemplateArgument; + + public NameTypeWithTemplateArguments(BaseNode Prev, BaseNode TemplateArgument) : base(NodeType.NameTypeWithTemplateArguments) + { + this.Prev = Prev; + this.TemplateArgument = TemplateArgument; + } + + public override string GetName() + { + return Prev.GetName(); + } + + public override void PrintLeft(TextWriter Writer) + { + Prev.Print(Writer); + TemplateArgument.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs new file mode 100644 index 0000000000..0ec6d98299 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NestedName.cs @@ -0,0 +1,26 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NestedName : ParentNode + { + private BaseNode Name; + + public NestedName(BaseNode Name, BaseNode Type) : base(NodeType.NestedName, Type) + { + this.Name = Name; + } + + public override string GetName() + { + return Name.GetName(); + } + + public override void PrintLeft(TextWriter Writer) + { + Child.Print(Writer); + Writer.Write("::"); + Name.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs new file mode 100644 index 0000000000..5cc14ad9f8 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NewExpression.cs @@ -0,0 +1,55 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NewExpression : BaseNode + { + private NodeArray Expressions; + private BaseNode TypeNode; + private NodeArray Initializers; + + private bool IsGlobal; + private bool IsArrayExpression; + + public NewExpression(NodeArray Expressions, BaseNode TypeNode, NodeArray Initializers, bool IsGlobal, bool IsArrayExpression) : base(NodeType.NewExpression) + { + this.Expressions = Expressions; + this.TypeNode = TypeNode; + this.Initializers = Initializers; + + this.IsGlobal = IsGlobal; + this.IsArrayExpression = IsArrayExpression; + } + + public override void PrintLeft(TextWriter Writer) + { + if (IsGlobal) + { + Writer.Write("::operator "); + } + + Writer.Write("new "); + + if (IsArrayExpression) + { + Writer.Write("[] "); + } + + if (Expressions.Nodes.Count != 0) + { + Writer.Write("("); + Expressions.Print(Writer); + Writer.Write(")"); + } + + TypeNode.Print(Writer); + + if (Initializers.Nodes.Count != 0) + { + Writer.Write("("); + Initializers.Print(Writer); + Writer.Write(")"); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs new file mode 100644 index 0000000000..f7bfa194fb --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NodeArray : BaseNode + { + public List Nodes { get; protected set; } + + public NodeArray(List Nodes) : base(NodeType.NodeArray) + { + this.Nodes = Nodes; + } + + public NodeArray(List Nodes, NodeType Type) : base(Type) + { + this.Nodes = Nodes; + } + + public override bool IsArray() + { + return true; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(string.Join(", ", Nodes.ToArray())); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs new file mode 100644 index 0000000000..5bee9cfacb --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NoexceptSpec.cs @@ -0,0 +1,16 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class NoexceptSpec : ParentNode + { + public NoexceptSpec(BaseNode Child) : base(NodeType.NoexceptSpec, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("noexcept("); + Child.Print(Writer); + Writer.Write(")"); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs new file mode 100644 index 0000000000..66ad112277 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameter.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PackedTemplateParameter : NodeArray + { + public PackedTemplateParameter(List Nodes) : base(Nodes, NodeType.PackedTemplateParameter) { } + + public override void PrintLeft(TextWriter Writer) + { + foreach (BaseNode Node in Nodes) + { + Node.PrintLeft(Writer); + } + } + + public override void PrintRight(TextWriter Writer) + { + foreach (BaseNode Node in Nodes) + { + Node.PrintLeft(Writer); + } + } + + public override bool HasRightPart() + { + foreach (BaseNode Node in Nodes) + { + if (Node.HasRightPart()) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs new file mode 100644 index 0000000000..ce9fa4a3ba --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PackedTemplateParameterExpansion.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PackedTemplateParameterExpansion : ParentNode + { + public PackedTemplateParameterExpansion(BaseNode Child) : base(NodeType.PackedTemplateParameterExpansion, Child) {} + + public override void PrintLeft(TextWriter Writer) + { + if (Child is PackedTemplateParameter) + { + if (((PackedTemplateParameter)Child).Nodes.Count != 0) + { + Child.Print(Writer); + } + } + else + { + Writer.Write("..."); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs new file mode 100644 index 0000000000..f1c2834739 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ParentNode.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public abstract class ParentNode : BaseNode + { + public BaseNode Child { get; private set; } + + public ParentNode(NodeType Type, BaseNode Child) : base(Type) + { + this.Child = Child; + } + + public override string GetName() + { + return Child.GetName(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs new file mode 100644 index 0000000000..a60776a272 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PointerType.cs @@ -0,0 +1,45 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PointerType : BaseNode + { + private BaseNode Child; + + public PointerType(BaseNode Child) : base(NodeType.PointerType) + { + this.Child = Child; + } + + public override bool HasRightPart() + { + return Child.HasRightPart(); + } + + public override void PrintLeft(TextWriter Writer) + { + Child.PrintLeft(Writer); + if (Child.IsArray()) + { + Writer.Write(" "); + } + + if (Child.IsArray() || Child.HasFunctions()) + { + Writer.Write("("); + } + + Writer.Write("*"); + } + + public override void PrintRight(TextWriter Writer) + { + if (Child.IsArray() || Child.HasFunctions()) + { + Writer.Write(")"); + } + + Child.PrintRight(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs new file mode 100644 index 0000000000..021f2de820 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixExpression.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PostfixExpression : ParentNode + { + private string Operator; + + public PostfixExpression(BaseNode Type, string Operator) : base(NodeType.PostfixExpression, Type) + { + this.Operator = Operator; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("("); + Child.Print(Writer); + Writer.Write(")"); + Writer.Write(Operator); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs new file mode 100644 index 0000000000..465450d3f2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PostfixQualifiedType.cs @@ -0,0 +1,20 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PostfixQualifiedType : ParentNode + { + private string PostfixQualifier; + + public PostfixQualifiedType(string PostfixQualifier, BaseNode Type) : base(NodeType.PostfixQualifiedType, Type) + { + this.PostfixQualifier = PostfixQualifier; + } + + public override void PrintLeft(TextWriter Writer) + { + Child.Print(Writer); + Writer.Write(PostfixQualifier); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs new file mode 100644 index 0000000000..619d05387b --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/PrefixExpression.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class PrefixExpression : ParentNode + { + private string Prefix; + + public PrefixExpression(string Prefix, BaseNode Child) : base(NodeType.PrefixExpression, Child) + { + this.Prefix = Prefix; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(Prefix); + Writer.Write("("); + Child.Print(Writer); + Writer.Write(")"); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs new file mode 100644 index 0000000000..ce356e1628 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/QualifiedName.cs @@ -0,0 +1,23 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class QualifiedName : BaseNode + { + private BaseNode Qualifier; + private BaseNode Name; + + public QualifiedName(BaseNode Qualifier, BaseNode Name) : base(NodeType.QualifiedName) + { + this.Qualifier = Qualifier; + this.Name = Name; + } + + public override void PrintLeft(TextWriter Writer) + { + Qualifier.Print(Writer); + Writer.Write("::"); + Name.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs new file mode 100644 index 0000000000..3721b8dee3 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/Qualifier.cs @@ -0,0 +1,120 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public enum CV + { + None, + Const, + Volatile, + Restricted = 4 + } + + public enum Reference + { + None, + RValue, + LValue + } + + public class CVType : ParentNode + { + public CV Qualifier; + + public CVType(CV Qualifier, BaseNode Child) : base(NodeType.CVQualifierType, Child) + { + this.Qualifier = Qualifier; + } + + public void PrintQualifier(TextWriter Writer) + { + if ((Qualifier & CV.Const) != 0) + { + Writer.Write(" const"); + } + + if ((Qualifier & CV.Volatile) != 0) + { + Writer.Write(" volatile"); + } + + if ((Qualifier & CV.Restricted) != 0) + { + Writer.Write(" restrict"); + } + } + + public override void PrintLeft(TextWriter Writer) + { + if (Child != null) + { + Child.PrintLeft(Writer); + } + + PrintQualifier(Writer); + } + + public override bool HasRightPart() + { + return Child != null && Child.HasRightPart(); + } + + public override void PrintRight(TextWriter Writer) + { + if (Child != null) + { + Child.PrintRight(Writer); + } + } + } + + public class SimpleReferenceType : ParentNode + { + public Reference Qualifier; + + public SimpleReferenceType(Reference Qualifier, BaseNode Child) : base(NodeType.SimpleReferenceType, Child) + { + this.Qualifier = Qualifier; + } + + public void PrintQualifier(TextWriter Writer) + { + if ((Qualifier & Reference.LValue) != 0) + { + Writer.Write("&"); + } + + if ((Qualifier & Reference.RValue) != 0) + { + Writer.Write("&&"); + } + } + + public override void PrintLeft(TextWriter Writer) + { + if (Child != null) + { + Child.PrintLeft(Writer); + } + else if (Qualifier != Reference.None) + { + Writer.Write(" "); + } + + PrintQualifier(Writer); + } + + public override bool HasRightPart() + { + return Child != null && Child.HasRightPart(); + } + + public override void PrintRight(TextWriter Writer) + { + if (Child != null) + { + Child.PrintRight(Writer); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs new file mode 100644 index 0000000000..602814afb3 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ReferenceType.cs @@ -0,0 +1,47 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ReferenceType : BaseNode + { + private string Reference; + private BaseNode Child; + + public ReferenceType(string Reference, BaseNode Child) : base(NodeType.ReferenceType) + { + this.Reference = Reference; + this.Child = Child; + } + + public override bool HasRightPart() + { + return Child.HasRightPart(); + } + + public override void PrintLeft(TextWriter Writer) + { + Child.PrintLeft(Writer); + + if (Child.IsArray()) + { + Writer.Write(" "); + } + + if (Child.IsArray() || Child.HasFunctions()) + { + Writer.Write("("); + } + + Writer.Write(Reference); + } + public override void PrintRight(TextWriter Writer) + { + if (Child.IsArray() || Child.HasFunctions()) + { + Writer.Write(")"); + } + + Child.PrintRight(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs new file mode 100644 index 0000000000..1a299af4d2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialName.cs @@ -0,0 +1,20 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class SpecialName : ParentNode + { + private string SpecialValue; + + public SpecialName(string SpecialValue, BaseNode Type) : base(NodeType.SpecialName, Type) + { + this.SpecialValue = SpecialValue; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write(SpecialValue); + Child.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs new file mode 100644 index 0000000000..f4e9a14a12 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/SpecialSubstitution.cs @@ -0,0 +1,89 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class SpecialSubstitution : BaseNode + { + public enum SpecialType + { + Allocator, + BasicString, + String, + IStream, + OStream, + IOStream, + } + + private SpecialType SpecialSubstitutionKey; + + public SpecialSubstitution(SpecialType SpecialSubstitutionKey) : base(NodeType.SpecialSubstitution) + { + this.SpecialSubstitutionKey = SpecialSubstitutionKey; + } + + public void SetExtended() + { + Type = NodeType.ExpandedSpecialSubstitution; + } + + public override string GetName() + { + switch (SpecialSubstitutionKey) + { + case SpecialType.Allocator: + return "allocator"; + case SpecialType.BasicString: + return "basic_string"; + case SpecialType.String: + if (Type == NodeType.ExpandedSpecialSubstitution) + { + return "basic_string"; + } + + return "string"; + case SpecialType.IStream: + return "istream"; + case SpecialType.OStream: + return "ostream"; + case SpecialType.IOStream: + return "iostream"; + } + + return null; + } + + private string GetExtendedName() + { + switch (SpecialSubstitutionKey) + { + case SpecialType.Allocator: + return "std::allocator"; + case SpecialType.BasicString: + return "std::basic_string"; + case SpecialType.String: + return "std::basic_string, std::allocator >"; + case SpecialType.IStream: + return "std::basic_istream >"; + case SpecialType.OStream: + return "std::basic_ostream >"; + case SpecialType.IOStream: + return "std::basic_iostream >"; + } + + return null; + } + + public override void PrintLeft(TextWriter Writer) + { + if (Type == NodeType.ExpandedSpecialSubstitution) + { + Writer.Write(GetExtendedName()); + } + else + { + Writer.Write("std::"); + Writer.Write(GetName()); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs new file mode 100644 index 0000000000..ed1b599429 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/StdQualifiedName.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class StdQualifiedName : ParentNode + { + public StdQualifiedName(BaseNode Child) : base(NodeType.StdQualifiedName, Child) { } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("std::"); + Child.Print(Writer); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs new file mode 100644 index 0000000000..d6efbd0fc3 --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class TemplateArguments : NodeArray + { + public TemplateArguments(List Nodes) : base(Nodes, NodeType.TemplateArguments) { } + + public override void PrintLeft(TextWriter Writer) + { + string Params = string.Join(", ", Nodes.ToArray()); + + Writer.Write("<"); + + Writer.Write(Params); + + if (Params.EndsWith(">")) + { + Writer.Write(" "); + } + + Writer.Write(">"); + } + } +} diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs new file mode 100644 index 0000000000..bb1466174e --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/ThrowExpression.cs @@ -0,0 +1,20 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast +{ + public class ThrowExpression : BaseNode + { + private BaseNode Expression; + + public ThrowExpression(BaseNode Expression) : base(NodeType.ThrowExpression) + { + this.Expression = Expression; + } + + public override void PrintLeft(TextWriter Writer) + { + Writer.Write("throw "); + Expression.Print(Writer); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs new file mode 100644 index 0000000000..164d5618ca --- /dev/null +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs @@ -0,0 +1,3367 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast; + +namespace Ryujinx.HLE.HOS.Diagnostics.Demangler +{ + class Demangler + { + private static readonly string BASE_36 = "0123456789abcdefghijklmnopqrstuvwxyz"; + private List SubstitutionList = new List(); + private List TemplateParamList = new List(); + + private List ForwardTemplateReferenceList = new List(); + + public string Mangled { get; private set; } + + private int Position; + private int Length; + + private bool CanForwardTemplateReference; + private bool CanParseTemplateArgs; + + public Demangler(string Mangled) + { + this.Mangled = Mangled; + Position = 0; + Length = Mangled.Length; + CanParseTemplateArgs = true; + } + + private bool ConsumeIf(string ToConsume) + { + string MangledPart = Mangled.Substring(Position); + + if (MangledPart.StartsWith(ToConsume)) + { + Position += ToConsume.Length; + + return true; + } + + return false; + } + + private string PeekString(int Offset = 0, int Length = 1) + { + if (Position + Offset >= Length) + { + return null; + } + + return Mangled.Substring(Position + Offset, Length); + } + + private char Peek(int Offset = 0) + { + if (Position + Offset >= Length) + { + return '\0'; + } + + return Mangled[Position + Offset]; + } + + private char Consume() + { + if (Position < Length) + { + return Mangled[Position++]; + } + + return '\0'; + } + + private int Count() + { + return Length - Position; + } + + private static int FromBase36(string Encoded) + { + char[] ReversedEncoded = Encoded.ToLower().ToCharArray().Reverse().ToArray(); + + int Result = 0; + + for (int i = 0; i < ReversedEncoded.Length; i++) + { + int Value = BASE_36.IndexOf(ReversedEncoded[i]); + if (Value == -1) + { + return -1; + } + + Result += Value * (int)Math.Pow(36, i); + } + + return Result; + } + + private int ParseSeqId() + { + string Part = Mangled.Substring(Position); + int SeqIdLen = 0; + + for (; SeqIdLen < Part.Length; SeqIdLen++) + { + if (!char.IsLetterOrDigit(Part[SeqIdLen])) + { + break; + } + } + + Position += SeqIdLen; + + return FromBase36(Part.Substring(0, SeqIdLen)); + } + + // ::= S _ + // ::= S_ + // ::= St # std:: + // ::= Sa # std::allocator + // ::= Sb # std::basic_string + // ::= Ss # std::basic_string, std::allocator > + // ::= Si # std::basic_istream > + // ::= So # std::basic_ostream > + // ::= Sd # std::basic_iostream > + private BaseNode ParseSubstitution() + { + if (!ConsumeIf("S")) + { + return null; + } + + char SubstitutionSecondChar = Peek(); + if (char.IsLower(SubstitutionSecondChar)) + { + switch (SubstitutionSecondChar) + { + case 'a': + Position++; + return new SpecialSubstitution(SpecialSubstitution.SpecialType.Allocator); + case 'b': + Position++; + return new SpecialSubstitution(SpecialSubstitution.SpecialType.BasicString); + case 's': + Position++; + return new SpecialSubstitution(SpecialSubstitution.SpecialType.String); + case 'i': + Position++; + return new SpecialSubstitution(SpecialSubstitution.SpecialType.IStream); + case 'o': + Position++; + return new SpecialSubstitution(SpecialSubstitution.SpecialType.OStream); + case 'd': + Position++; + return new SpecialSubstitution(SpecialSubstitution.SpecialType.IOStream); + default: + return null; + } + } + + // ::= S_ + if (ConsumeIf("_")) + { + if (SubstitutionList.Count != 0) + { + return SubstitutionList[0]; + } + + return null; + } + + // ::= S _ + int SeqId = ParseSeqId(); + if (SeqId < 0) + { + return null; + } + + SeqId++; + + if (!ConsumeIf("_") || SeqId >= SubstitutionList.Count) + { + return null; + } + + return SubstitutionList[SeqId]; + } + + // NOTE: thoses data aren't used in the output + // ::= h _ + // ::= v _ + // ::= + // # non-virtual base override + // ::= _ + // # virtual base override, with vcall offset + private bool ParseCallOffset() + { + if (ConsumeIf("h")) + { + return ParseNumber(true).Length == 0 || !ConsumeIf("_"); + } + else if (ConsumeIf("v")) + { + return ParseNumber(true).Length == 0 || !ConsumeIf("_") || ParseNumber(true).Length == 0 || !ConsumeIf("_"); + } + + return true; + } + + + // ::= # non-dependent type name, dependent type name, or dependent typename-specifier + // ::= Ts # dependent elaborated type specifier using 'struct' or 'class' + // ::= Tu # dependent elaborated type specifier using 'union' + // ::= Te # dependent elaborated type specifier using 'enum' + private BaseNode ParseClassEnumType() + { + string ElaboratedType = null; + + if (ConsumeIf("Ts")) + { + ElaboratedType = "struct"; + } + else if (ConsumeIf("Tu")) + { + ElaboratedType = "union"; + } + else if (ConsumeIf("Te")) + { + ElaboratedType = "enum"; + } + + BaseNode Name = ParseName(); + if (Name == null) + { + return null; + } + + if (ElaboratedType == null) + { + return Name; + } + + return new ElaboratedType(ElaboratedType, Name); + } + + // ::= [] [] [Dx] F [Y] [] E + // ::= + + // # types are possible return type, then parameter types + // ::= Do # non-throwing exception-specification (e.g., noexcept, throw()) + // ::= DO E # computed (instantiation-dependent) noexcept + // ::= Dw + E # dynamic exception specification with instantiation-dependent types + private BaseNode ParseFunctionType() + { + CV CVQualifiers = ParseCVQualifiers(); + + BaseNode ExceptionSpec = null; + + if (ConsumeIf("Do")) + { + ExceptionSpec = new NameType("noexcept"); + } + else if (ConsumeIf("DO")) + { + BaseNode Expression = ParseExpression(); + if (Expression == null || !ConsumeIf("E")) + { + return null; + } + + ExceptionSpec = new NoexceptSpec(Expression); + } + else if (ConsumeIf("Dw")) + { + List Types = new List(); + + while (!ConsumeIf("E")) + { + BaseNode Type = ParseType(); + if (Type == null) + { + return null; + } + + Types.Add(Type); + } + + ExceptionSpec = new DynamicExceptionSpec(new NodeArray(Types)); + } + + // We don't need the transaction + ConsumeIf("Dx"); + + if (!ConsumeIf("F")) + { + return null; + } + + // extern "C" + ConsumeIf("Y"); + + BaseNode ReturnType = ParseType(); + if (ReturnType == null) + { + return null; + } + + Reference ReferenceQualifier = Reference.None; + List Params = new List(); + + while (true) + { + if (ConsumeIf("E")) + { + break; + } + + if (ConsumeIf("v")) + { + continue; + } + + if (ConsumeIf("RE")) + { + ReferenceQualifier = Reference.LValue; + break; + } + else if (ConsumeIf("OE")) + { + ReferenceQualifier = Reference.RValue; + break; + } + + BaseNode Type = ParseType(); + if (Type == null) + { + return null; + } + + Params.Add(Type); + } + + return new FunctionType(ReturnType, new NodeArray(Params), new CVType(CVQualifiers, null), new SimpleReferenceType(ReferenceQualifier, null), ExceptionSpec); + } + + // ::= A _ + // ::= A [] _ + private BaseNode ParseArrayType() + { + if (!ConsumeIf("A")) + { + return null; + } + + BaseNode ElementType; + if (char.IsDigit(Peek())) + { + string Dimension = ParseNumber(); + if (Dimension.Length == 0 || !ConsumeIf("_")) + { + return null; + } + + ElementType = ParseType(); + if (ElementType == null) + { + return null; + } + + return new ArrayType(ElementType, Dimension); + } + + if (!ConsumeIf("_")) + { + BaseNode DimensionExpression = ParseExpression(); + if (DimensionExpression == null || !ConsumeIf("_")) + { + return null; + } + + ElementType = ParseType(); + if (ElementType == null) + { + return null; + } + + return new ArrayType(ElementType, DimensionExpression); + } + + ElementType = ParseType(); + if (ElementType == null) + { + return null; + } + + return new ArrayType(ElementType); + } + + // ::= + // ::= (PARTIAL) + // ::= + // ::= + // ::= (TODO) + // ::= (TODO) + // ::= + // ::= + // ::= + // ::= P # pointer + // ::= R # l-value reference + // ::= O # r-value reference (C++11) + // ::= C # complex pair (C99) + // ::= G # imaginary (C99) + // ::= # See Compression below + private BaseNode ParseType(NameParserContext Context = null) + { + // Temporary context + if (Context == null) + { + Context = new NameParserContext(); + } + + BaseNode Result = null; + switch (Peek()) + { + case 'r': + case 'V': + case 'K': + int TypePos = 0; + + if (Peek(TypePos) == 'r') + { + TypePos++; + } + + if (Peek(TypePos) == 'V') + { + TypePos++; + } + + if (Peek(TypePos) == 'K') + { + TypePos++; + } + + if (Peek(TypePos) == 'F' || (Peek(TypePos) == 'D' && (Peek(TypePos + 1) == 'o' || Peek(TypePos + 1) == 'O' || Peek(TypePos + 1) == 'w' || Peek(TypePos + 1) == 'x'))) + { + Result = ParseFunctionType(); + break; + } + + CV CV = ParseCVQualifiers(); + + Result = ParseType(Context); + + if (Result == null) + { + return null; + } + + Result = new CVType(CV, Result); + break; + case 'U': + // TODO: + return null; + case 'v': + Position++; + return new NameType("void"); + case 'w': + Position++; + return new NameType("wchar_t"); + case 'b': + Position++; + return new NameType("bool"); + case 'c': + Position++; + return new NameType("char"); + case 'a': + Position++; + return new NameType("signed char"); + case 'h': + Position++; + return new NameType("unsigned char"); + case 's': + Position++; + return new NameType("short"); + case 't': + Position++; + return new NameType("unsigned short"); + case 'i': + Position++; + return new NameType("int"); + case 'j': + Position++; + return new NameType("unsigned int"); + case 'l': + Position++; + return new NameType("long"); + case 'm': + Position++; + return new NameType("unsigned long"); + case 'x': + Position++; + return new NameType("long long"); + case 'y': + Position++; + return new NameType("unsigned long long"); + case 'n': + Position++; + return new NameType("__int128"); + case 'o': + Position++; + return new NameType("unsigned __int128"); + case 'f': + Position++; + return new NameType("float"); + case 'd': + Position++; + return new NameType("double"); + case 'e': + Position++; + return new NameType("long double"); + case 'g': + Position++; + return new NameType("__float128"); + case 'z': + Position++; + return new NameType("..."); + case 'u': + Position++; + return ParseSourceName(); + case 'D': + switch (Peek(1)) + { + case 'd': + Position += 2; + return new NameType("decimal64"); + case 'e': + Position += 2; + return new NameType("decimal128"); + case 'f': + Position += 2; + return new NameType("decimal32"); + case 'h': + Position += 2; + // FIXME: GNU c++flit returns this but that is not what is supposed to be returned. + return new NameType("half"); + //return new NameType("decimal16"); + case 'i': + Position += 2; + return new NameType("char32_t"); + case 's': + Position += 2; + return new NameType("char16_t"); + case 'a': + Position += 2; + return new NameType("decltype(auto)"); + case 'n': + Position += 2; + // FIXME: GNU c++flit returns this but that is not what is supposed to be returned. + return new NameType("decltype(nullptr)"); + //return new NameType("std::nullptr_t"); + case 't': + case 'T': + Position += 2; + Result = ParseDecltype(); + break; + case 'o': + case 'O': + case 'w': + case 'x': + Result = ParseFunctionType(); + break; + default: + return null; + } + break; + case 'F': + Result = ParseFunctionType(); + break; + case 'A': + return ParseArrayType(); + case 'M': + // TODO: + Position++; + return null; + case 'T': + // might just be a class enum type + if (Peek(1) == 's' || Peek(1) == 'u' || Peek(1) == 'e') + { + Result = ParseClassEnumType(); + break; + } + + Result = ParseTemplateParam(); + if (Result == null) + { + return null; + } + + if (CanParseTemplateArgs && Peek() == 'I') + { + BaseNode TemplateArguments = ParseTemplateArguments(); + if (TemplateArguments == null) + { + return null; + } + + Result = new NameTypeWithTemplateArguments(Result, TemplateArguments); + } + break; + case 'P': + Position++; + Result = ParseType(Context); + + if (Result == null) + { + return null; + } + + Result = new PointerType(Result); + break; + case 'R': + Position++; + Result = ParseType(Context); + + if (Result == null) + { + return null; + } + + Result = new ReferenceType("&", Result); + break; + case 'O': + Position++; + Result = ParseType(Context); + + if (Result == null) + { + return null; + } + + Result = new ReferenceType("&&", Result); + break; + case 'C': + Position++; + Result = ParseType(Context); + + if (Result == null) + { + return null; + } + + Result = new PostfixQualifiedType(" complex", Result); + break; + case 'G': + Position++; + Result = ParseType(Context); + + if (Result == null) + { + return null; + } + + Result = new PostfixQualifiedType(" imaginary", Result); + break; + case 'S': + if (Peek(1) != 't') + { + BaseNode Substitution = ParseSubstitution(); + if (Substitution == null) + { + return null; + } + + if (CanParseTemplateArgs && Peek() == 'I') + { + BaseNode TemplateArgument = ParseTemplateArgument(); + if (TemplateArgument == null) + { + return null; + } + + Result = new NameTypeWithTemplateArguments(Substitution, TemplateArgument); + break; + } + return Substitution; + } + else + { + Result = ParseClassEnumType(); + break; + } + default: + Result = ParseClassEnumType(); + break; + } + if (Result != null) + { + SubstitutionList.Add(Result); + } + + return Result; + } + + // ::= TV # virtual table + // ::= TT # VTT structure (construction vtable index) + // ::= TI # typeinfo structure + // ::= TS # typeinfo name (null-terminated byte string) + // ::= Tc + // ::= TW # Thread-local wrapper + // ::= TH # Thread-local initialization + // ::= T + // # base is the nominal target function of thunk + // ::= GV # Guard variable for one-time initialization + private BaseNode ParseSpecialName(NameParserContext Context = null) + { + if (Peek() != 'T') + { + if (ConsumeIf("GV")) + { + BaseNode Name = ParseName(); + if (Name == null) + { + return null; + } + + return new SpecialName("guard variable for ", Name); + } + return null; + } + + BaseNode Node; + switch (Peek(1)) + { + // ::= TV # virtual table + case 'V': + Position += 2; + Node = ParseType(Context); + if (Node == null) + { + return null; + } + + return new SpecialName("vtable for ", Node); + // ::= TT # VTT structure (construction vtable index) + case 'T': + Position += 2; + Node = ParseType(Context); + if (Node == null) + { + return null; + } + + return new SpecialName("VTT for ", Node); + // ::= TI # typeinfo structure + case 'I': + Position += 2; + Node = ParseType(Context); + if (Node == null) + { + return null; + } + + return new SpecialName("typeinfo for ", Node); + // ::= TS # typeinfo name (null-terminated byte string) + case 'S': + Position += 2; + Node = ParseType(Context); + if (Node == null) + { + return null; + } + + return new SpecialName("typeinfo name for ", Node); + // ::= Tc + case 'c': + Position += 2; + if (ParseCallOffset() || ParseCallOffset()) + { + return null; + } + + Node = ParseEncoding(); + if (Node == null) + { + return null; + } + + return new SpecialName("covariant return thunk to ", Node); + // extension ::= TC _ + case 'C': + Position += 2; + BaseNode FirstType = ParseType(); + if (FirstType == null || ParseNumber(true).Length == 0 || !ConsumeIf("_")) + { + return null; + } + + BaseNode SecondType = ParseType(); + + return new CtorVtableSpecialName(SecondType, FirstType); + // ::= TH # Thread-local initialization + case 'H': + Position += 2; + Node = ParseName(); + if (Node == null) + { + return null; + } + + return new SpecialName("thread-local initialization routine for ", Node); + // ::= TW # Thread-local wrapper + case 'W': + Position += 2; + Node = ParseName(); + if (Node == null) + { + return null; + } + + return new SpecialName("thread-local wrapper routine for ", Node); + default: + Position++; + bool IsVirtual = Peek() == 'v'; + if (ParseCallOffset()) + { + return null; + } + + Node = ParseEncoding(); + if (Node == null) + { + return null; + } + + if (IsVirtual) + { + return new SpecialName("virtual thunk to ", Node); + } + + return new SpecialName("non-virtual thunk to ", Node); + } + } + + // ::= [r] [V] [K] # restrict (C99), volatile, const + private CV ParseCVQualifiers() + { + CV Qualifiers = CV.None; + + if (ConsumeIf("r")) + { + Qualifiers |= CV.Restricted; + } + if (ConsumeIf("V")) + { + Qualifiers |= CV.Volatile; + } + if (ConsumeIf("K")) + { + Qualifiers |= CV.Const; + } + + return Qualifiers; + } + + + // ::= R # & ref-qualifier + // ::= O # && ref-qualifier + private SimpleReferenceType ParseRefQualifiers() + { + Reference Result = Reference.None; + if (ConsumeIf("O")) + { + Result = Reference.RValue; + } + else if (ConsumeIf("R")) + { + Result = Reference.LValue; + } + return new SimpleReferenceType(Result, null); + } + + private BaseNode CreateNameNode(BaseNode Prev, BaseNode Name, NameParserContext Context) + { + BaseNode Result = Name; + if (Prev != null) + { + Result = new NestedName(Name, Prev); + } + + if (Context != null) + { + Context.FinishWithTemplateArguments = false; + } + + return Result; + } + + private int ParsePositiveNumber() + { + string Part = Mangled.Substring(Position); + int NumberLength = 0; + + for (; NumberLength < Part.Length; NumberLength++) + { + if (!char.IsDigit(Part[NumberLength])) + { + break; + } + } + + Position += NumberLength; + + if (NumberLength == 0) + { + return -1; + } + + return int.Parse(Part.Substring(0, NumberLength)); + } + + private string ParseNumber(bool IsSigned = false) + { + if (IsSigned) + { + ConsumeIf("n"); + } + + if (Count() == 0 || !char.IsDigit(Mangled[Position])) + { + return null; + } + + string Part = Mangled.Substring(Position); + int NumberLength = 0; + + for (; NumberLength < Part.Length; NumberLength++) + { + if (!char.IsDigit(Part[NumberLength])) + { + break; + } + } + + Position += NumberLength; + + return Part.Substring(0, NumberLength); + } + + // ::= + private BaseNode ParseSourceName() + { + int Length = ParsePositiveNumber(); + if (Count() < Length || Length <= 0) + { + return null; + } + + string Name = Mangled.Substring(Position, Length); + Position += Length; + if (Name.StartsWith("_GLOBAL__N")) + { + return new NameType("(anonymous namespace)"); + } + + return new NameType(Name); + } + + // ::= nw # new + // ::= na # new[] + // ::= dl # delete + // ::= da # delete[] + // ::= ps # + (unary) + // ::= ng # - (unary) + // ::= ad # & (unary) + // ::= de # * (unary) + // ::= co # ~ + // ::= pl # + + // ::= mi # - + // ::= ml # * + // ::= dv # / + // ::= rm # % + // ::= an # & + // ::= or # | + // ::= eo # ^ + // ::= aS # = + // ::= pL # += + // ::= mI # -= + // ::= mL # *= + // ::= dV # /= + // ::= rM # %= + // ::= aN # &= + // ::= oR # |= + // ::= eO # ^= + // ::= ls # << + // ::= rs # >> + // ::= lS # <<= + // ::= rS # >>= + // ::= eq # == + // ::= ne # != + // ::= lt # < + // ::= gt # > + // ::= le # <= + // ::= ge # >= + // ::= ss # <=> + // ::= nt # ! + // ::= aa # && + // ::= oo # || + // ::= pp # ++ (postfix in context) + // ::= mm # -- (postfix in context) + // ::= cm # , + // ::= pm # ->* + // ::= pt # -> + // ::= cl # () + // ::= ix # [] + // ::= qu # ? + // ::= cv # (cast) (TODO) + // ::= li # operator "" + // ::= v # vendor extended operator (TODO) + private BaseNode ParseOperatorName(NameParserContext Context) + { + switch (Peek()) + { + case 'a': + switch (Peek(1)) + { + case 'a': + Position += 2; + return new NameType("operator&&"); + case 'd': + case 'n': + Position += 2; + return new NameType("operator&"); + case 'N': + Position += 2; + return new NameType("operator&="); + case 'S': + Position += 2; + return new NameType("operator="); + default: + return null; + } + case 'c': + switch (Peek(1)) + { + case 'l': + Position += 2; + return new NameType("operator()"); + case 'm': + Position += 2; + return new NameType("operator,"); + case 'o': + Position += 2; + return new NameType("operator~"); + case 'v': + Position += 2; + + bool CanParseTemplateArgsBackup = CanParseTemplateArgs; + bool CanForwardTemplateReferenceBackup = CanForwardTemplateReference; + + CanParseTemplateArgs = false; + CanForwardTemplateReference = CanForwardTemplateReferenceBackup || Context != null; + + BaseNode Type = ParseType(); + + CanParseTemplateArgs = CanParseTemplateArgsBackup; + CanForwardTemplateReference = CanForwardTemplateReferenceBackup; + + if (Type == null) + { + return null; + } + + if (Context != null) + { + Context.CtorDtorConversion = true; + } + + return new ConversionOperatorType(Type); + default: + return null; + } + case 'd': + switch (Peek(1)) + { + case 'a': + Position += 2; + return new NameType("operator delete[]"); + case 'e': + Position += 2; + return new NameType("operator*"); + case 'l': + Position += 2; + return new NameType("operator delete"); + case 'v': + Position += 2; + return new NameType("operator/"); + case 'V': + Position += 2; + return new NameType("operator/="); + default: + return null; + } + case 'e': + switch (Peek(1)) + { + case 'o': + Position += 2; + return new NameType("operator^"); + case 'O': + Position += 2; + return new NameType("operator^="); + case 'q': + Position += 2; + return new NameType("operator=="); + default: + return null; + } + case 'g': + switch (Peek(1)) + { + case 'e': + Position += 2; + return new NameType("operator>="); + case 't': + Position += 2; + return new NameType("operator>"); + default: + return null; + } + case 'i': + if (Peek(1) == 'x') + { + Position += 2; + return new NameType("operator[]"); + } + return null; + case 'l': + switch (Peek(1)) + { + case 'e': + Position += 2; + return new NameType("operator<="); + case 'i': + Position += 2; + BaseNode SourceName = ParseSourceName(); + if (SourceName == null) + { + return null; + } + + return new LiteralOperator(SourceName); + case 's': + Position += 2; + return new NameType("operator<<"); + case 'S': + Position += 2; + return new NameType("operator<<="); + case 't': + Position += 2; + return new NameType("operator<"); + default: + return null; + } + case 'm': + switch (Peek(1)) + { + case 'i': + Position += 2; + return new NameType("operator-"); + case 'I': + Position += 2; + return new NameType("operator-="); + case 'l': + Position += 2; + return new NameType("operator*"); + case 'L': + Position += 2; + return new NameType("operator*="); + case 'm': + Position += 2; + return new NameType("operator--"); + default: + return null; + } + case 'n': + switch (Peek(1)) + { + case 'a': + Position += 2; + return new NameType("operator new[]"); + case 'e': + Position += 2; + return new NameType("operator!="); + case 'g': + Position += 2; + return new NameType("operator-"); + case 't': + Position += 2; + return new NameType("operator!"); + case 'w': + Position += 2; + return new NameType("operator new"); + default: + return null; + } + case 'o': + switch (Peek(1)) + { + case 'o': + Position += 2; + return new NameType("operator||"); + case 'r': + Position += 2; + return new NameType("operator|"); + case 'R': + Position += 2; + return new NameType("operator|="); + default: + return null; + } + case 'p': + switch (Peek(1)) + { + case 'm': + Position += 2; + return new NameType("operator->*"); + case 's': + case 'l': + Position += 2; + return new NameType("operator+"); + case 'L': + Position += 2; + return new NameType("operator+="); + case 'p': + Position += 2; + return new NameType("operator++"); + case 't': + Position += 2; + return new NameType("operator->"); + default: + return null; + } + case 'q': + if (Peek(1) == 'u') + { + Position += 2; + return new NameType("operator?"); + } + return null; + case 'r': + switch (Peek(1)) + { + case 'm': + Position += 2; + return new NameType("operator%"); + case 'M': + Position += 2; + return new NameType("operator%="); + case 's': + Position += 2; + return new NameType("operator>>"); + case 'S': + Position += 2; + return new NameType("operator>>="); + default: + return null; + } + case 's': + if (Peek(1) == 's') + { + Position += 2; + return new NameType("operator<=>"); + } + return null; + case 'v': + // TODO: ::= v # vendor extended operator + return null; + default: + return null; + } + } + + // ::= [ (TODO)] + // ::= (TODO) + // ::= + // ::= (TODO) + // ::= DC + E # structured binding declaration (TODO) + private BaseNode ParseUnqualifiedName(NameParserContext Context) + { + BaseNode Result = null; + char C = Peek(); + if (C == 'U') + { + // TODO: Unnamed Type Name + // throw new Exception("Unnamed Type Name not implemented"); + } + else if (char.IsDigit(C)) + { + Result = ParseSourceName(); + } + else if (ConsumeIf("DC")) + { + // TODO: Structured Binding Declaration + // throw new Exception("Structured Binding Declaration not implemented"); + } + else + { + Result = ParseOperatorName(Context); + } + + if (Result != null) + { + // TODO: ABI Tags + //throw new Exception("ABI Tags not implemented"); + } + return Result; + } + + // ::= C1 # complete object constructor + // ::= C2 # base object constructor + // ::= C3 # complete object allocating constructor + // ::= D0 # deleting destructor + // ::= D1 # complete object destructor + // ::= D2 # base object destructor + private BaseNode ParseCtorDtorName(NameParserContext Context, BaseNode Prev) + { + if (Prev.Type == NodeType.SpecialSubstitution && Prev is SpecialSubstitution) + { + ((SpecialSubstitution)Prev).SetExtended(); + } + + if (ConsumeIf("C")) + { + bool IsInherited = ConsumeIf("I"); + + char CtorDtorType = Peek(); + if (CtorDtorType != '1' && CtorDtorType != '2' && CtorDtorType != '3') + { + return null; + } + + Position++; + + if (Context != null) + { + Context.CtorDtorConversion = true; + } + + if (IsInherited && ParseName(Context) == null) + { + return null; + } + + return new CtorDtorNameType(Prev, false); + } + + if (ConsumeIf("D")) + { + char C = Peek(); + if (C != '0' && C != '1' && C != '2') + { + return null; + } + + Position++; + + if (Context != null) + { + Context.CtorDtorConversion = true; + } + + return new CtorDtorNameType(Prev, true); + } + + return null; + } + + // ::= fp _ # L == 0, first parameter + // ::= fp _ # L == 0, second and later parameters + // ::= fL p _ # L > 0, first parameter + // ::= fL p _ # L > 0, second and later parameters + private BaseNode ParseFunctionParameter() + { + if (ConsumeIf("fp")) + { + // ignored + ParseCVQualifiers(); + + if (!ConsumeIf("_")) + { + return null; + } + + return new FunctionParameter(ParseNumber()); + } + else if (ConsumeIf("fL")) + { + string L1Number = ParseNumber(); + if (L1Number == null || L1Number.Length == 0) + { + return null; + } + + if (!ConsumeIf("p")) + { + return null; + } + + // ignored + ParseCVQualifiers(); + + if (!ConsumeIf("_")) + { + return null; + } + + return new FunctionParameter(ParseNumber()); + } + + return null; + } + + // ::= fL + // ::= fR + // ::= fl + // ::= fr + private BaseNode ParseFoldExpression() + { + if (!ConsumeIf("f")) + { + return null; + } + + char FoldKind = Peek(); + bool HasInitializer = FoldKind == 'L' || FoldKind == 'R'; + bool IsLeftFold = FoldKind == 'l' || FoldKind == 'L'; + + if (!IsLeftFold && !(FoldKind == 'r' || FoldKind == 'R')) + { + return null; + } + + Position++; + + string OperatorName = null; + + switch (PeekString(0, 2)) + { + case "aa": + OperatorName = "&&"; + break; + case "an": + OperatorName = "&"; + break; + case "aN": + OperatorName = "&="; + break; + case "aS": + OperatorName = "="; + break; + case "cm": + OperatorName = ","; + break; + case "ds": + OperatorName = ".*"; + break; + case "dv": + OperatorName = "/"; + break; + case "dV": + OperatorName = "/="; + break; + case "eo": + OperatorName = "^"; + break; + case "eO": + OperatorName = "^="; + break; + case "eq": + OperatorName = "=="; + break; + case "ge": + OperatorName = ">="; + break; + case "gt": + OperatorName = ">"; + break; + case "le": + OperatorName = "<="; + break; + case "ls": + OperatorName = "<<"; + break; + case "lS": + OperatorName = "<<="; + break; + case "lt": + OperatorName = "<"; + break; + case "mi": + OperatorName = "-"; + break; + case "mI": + OperatorName = "-="; + break; + case "ml": + OperatorName = "*"; + break; + case "mL": + OperatorName = "*="; + break; + case "ne": + OperatorName = "!="; + break; + case "oo": + OperatorName = "||"; + break; + case "or": + OperatorName = "|"; + break; + case "oR": + OperatorName = "|="; + break; + case "pl": + OperatorName = "+"; + break; + case "pL": + OperatorName = "+="; + break; + case "rm": + OperatorName = "%"; + break; + case "rM": + OperatorName = "%="; + break; + case "rs": + OperatorName = ">>"; + break; + case "rS": + OperatorName = ">>="; + break; + default: + return null; + } + + Position += 2; + + BaseNode Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + BaseNode Initializer = null; + + if (HasInitializer) + { + Initializer = ParseExpression(); + if (Initializer == null) + { + return null; + } + } + + if (IsLeftFold && Initializer != null) + { + BaseNode Temp = Expression; + Expression = Initializer; + Initializer = Temp; + } + + return new FoldExpression(IsLeftFold, OperatorName, new PackedTemplateParameterExpansion(Expression), Initializer); + } + + + // ::= cv # type (expression), conversion with one argument + // ::= cv _ * E # type (expr-list), conversion with other than one argument + private BaseNode ParseConversionExpression() + { + if (!ConsumeIf("cv")) + { + return null; + } + + bool CanParseTemplateArgsBackup = CanParseTemplateArgs; + CanParseTemplateArgs = false; + BaseNode Type = ParseType(); + CanParseTemplateArgs = CanParseTemplateArgsBackup; + + if (Type == null) + { + return null; + } + + List Expressions = new List(); + if (ConsumeIf("_")) + { + while (!ConsumeIf("E")) + { + BaseNode Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + Expressions.Add(Expression); + } + } + else + { + BaseNode Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + Expressions.Add(Expression); + } + + return new ConversionExpression(Type, new NodeArray(Expressions)); + } + + private BaseNode ParseBinaryExpression(string Name) + { + BaseNode LeftPart = ParseExpression(); + if (LeftPart == null) + { + return null; + } + + BaseNode RightPart = ParseExpression(); + if (RightPart == null) + { + return null; + } + + return new BinaryExpression(LeftPart, Name, RightPart); + } + + private BaseNode ParsePrefixExpression(string Name) + { + BaseNode Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new PrefixExpression(Name, Expression); + } + + + // ::= + // ::= di # .name = expr + // ::= dx # [expr] = expr + // ::= dX + // # [expr ... expr] = expr + private BaseNode ParseBracedExpression() + { + if (Peek() == 'd') + { + BaseNode BracedExpressionNode; + switch (Peek(1)) + { + case 'i': + Position += 2; + BaseNode Field = ParseSourceName(); + if (Field == null) + { + return null; + } + + BracedExpressionNode = ParseBracedExpression(); + if (BracedExpressionNode == null) + { + return null; + } + + return new BracedExpression(Field, BracedExpressionNode, false); + case 'x': + Position += 2; + BaseNode Index = ParseExpression(); + if (Index == null) + { + return null; + } + + BracedExpressionNode = ParseBracedExpression(); + if (BracedExpressionNode == null) + { + return null; + } + + return new BracedExpression(Index, BracedExpressionNode, true); + case 'X': + Position += 2; + BaseNode RangeBeginExpression = ParseExpression(); + if (RangeBeginExpression == null) + { + return null; + } + + BaseNode RangeEndExpression = ParseExpression(); + if (RangeEndExpression == null) + { + return null; + } + + BracedExpressionNode = ParseBracedExpression(); + if (BracedExpressionNode == null) + { + return null; + } + + return new BracedRangeExpression(RangeBeginExpression, RangeEndExpression, BracedExpressionNode); + } + } + + return ParseExpression(); + } + + // ::= [gs] nw * _ E # new (expr-list) type + // ::= [gs] nw * _ # new (expr-list) type (init) + // ::= [gs] na * _ E # new[] (expr-list) type + // ::= [gs] na * _ # new[] (expr-list) type (init) + // + // ::= pi * E # parenthesized initialization + private BaseNode ParseNewExpression() + { + bool IsGlobal = ConsumeIf("gs"); + bool IsArray = Peek(1) == 'a'; + + if (!ConsumeIf("nw") || !ConsumeIf("na")) + { + return null; + } + + List Expressions = new List(); + List Initializers = new List(); + + while (!ConsumeIf("_")) + { + BaseNode Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + Expressions.Add(Expression); + } + + BaseNode TypeNode = ParseType(); + if (TypeNode == null) + { + return null; + } + + if (ConsumeIf("pi")) + { + while (!ConsumeIf("E")) + { + BaseNode Initializer = ParseExpression(); + if (Initializer == null) + { + return null; + } + + Initializers.Add(Initializer); + } + } + else if (!ConsumeIf("E")) + { + return null; + } + + return new NewExpression(new NodeArray(Expressions), TypeNode, new NodeArray(Initializers), IsGlobal, IsArray); + } + + + // ::= + // ::= + // ::= + // ::= pp_ # prefix ++ + // ::= mm_ # prefix -- + // ::= cl + E # expression (expr-list), call + // ::= cv # type (expression), conversion with one argument + // ::= cv _ * E # type (expr-list), conversion with other than one argument + // ::= tl * E # type {expr-list}, conversion with braced-init-list argument + // ::= il * E # {expr-list}, braced-init-list in any other context + // ::= [gs] nw * _ E # new (expr-list) type + // ::= [gs] nw * _ # new (expr-list) type (init) + // ::= [gs] na * _ E # new[] (expr-list) type + // ::= [gs] na * _ # new[] (expr-list) type (init) + // ::= [gs] dl # delete expression + // ::= [gs] da # delete[] expression + // ::= dc # dynamic_cast (expression) + // ::= sc # static_cast (expression) + // ::= cc # const_cast (expression) + // ::= rc # reinterpret_cast (expression) + // ::= ti # typeid (type) + // ::= te # typeid (expression) + // ::= st # sizeof (type) + // ::= sz # sizeof (expression) + // ::= at # alignof (type) + // ::= az # alignof (expression) + // ::= nx # noexcept (expression) + // ::= + // ::= + // ::= dt # expr.name + // ::= pt # expr->name + // ::= ds # expr.*expr + // ::= sZ # sizeof...(T), size of a template parameter pack + // ::= sZ # sizeof...(parameter), size of a function parameter pack + // ::= sP * E # sizeof...(T), size of a captured template parameter pack from an alias template + // ::= sp # expression..., pack expansion + // ::= tw # throw expression + // ::= tr # throw with no operand (rethrow) + // ::= # f(p), N::f(p), ::f(p), + // # freestanding dependent name (e.g., T::x), + // # objectless nonstatic member reference + // ::= + private BaseNode ParseExpression() + { + bool IsGlobal = ConsumeIf("gs"); + BaseNode Expression = null; + if (Count() < 2) + { + return null; + } + + switch (Peek()) + { + case 'L': + return ParseExpressionPrimary(); + case 'T': + return ParseTemplateParam(); + case 'f': + char C = Peek(1); + if (C == 'p' || (C == 'L' && char.IsDigit(Peek(2)))) + { + return ParseFunctionParameter(); + } + + return ParseFoldExpression(); + case 'a': + switch (Peek(1)) + { + case 'a': + Position += 2; + return ParseBinaryExpression("&&"); + case 'd': + case 'n': + Position += 2; + return ParseBinaryExpression("&"); + case 'N': + Position += 2; + return ParseBinaryExpression("&="); + case 'S': + Position += 2; + return ParseBinaryExpression("="); + case 't': + Position += 2; + BaseNode Type = ParseType(); + if (Type == null) + { + return null; + } + + return new EnclosedExpression("alignof (", Type, ")"); + case 'z': + Position += 2; + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new EnclosedExpression("alignof (", Expression, ")"); + } + return null; + case 'c': + switch (Peek(1)) + { + case 'c': + Position += 2; + BaseNode To = ParseType(); + if (To == null) + { + return null; + } + + BaseNode From = ParseExpression(); + if (From == null) + { + return null; + } + + return new CastExpression("const_cast", To, From); + case 'l': + Position += 2; + BaseNode Callee = ParseExpression(); + if (Callee == null) + { + return null; + } + + List Names = new List(); + while (!ConsumeIf("E")) + { + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + Names.Add(Expression); + } + return new CallExpression(Callee, Names); + case 'm': + Position += 2; + return ParseBinaryExpression(","); + case 'o': + Position += 2; + return ParsePrefixExpression("~"); + case 'v': + return ParseConversionExpression(); + } + return null; + case 'd': + BaseNode LeftNode = null; + BaseNode RightNode = null; + switch (Peek(1)) + { + case 'a': + Position += 2; + Expression = ParseExpression(); + if (Expression == null) + { + return Expression; + } + + return new DeleteExpression(Expression, IsGlobal, true); + case 'c': + Position += 2; + BaseNode Type = ParseType(); + if (Type == null) + { + return null; + } + + Expression = ParseExpression(); + if (Expression == null) + { + return Expression; + } + + return new CastExpression("dynamic_cast", Type, Expression); + case 'e': + Position += 2; + return ParsePrefixExpression("*"); + case 'l': + Position += 2; + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new DeleteExpression(Expression, IsGlobal, false); + case 'n': + return ParseUnresolvedName(); + case 's': + Position += 2; + LeftNode = ParseExpression(); + if (LeftNode == null) + { + return null; + } + + RightNode = ParseExpression(); + if (RightNode == null) + { + return null; + } + + return new MemberExpression(LeftNode, ".*", RightNode); + case 't': + Position += 2; + LeftNode = ParseExpression(); + if (LeftNode == null) + { + return null; + } + + RightNode = ParseExpression(); + if (RightNode == null) + { + return null; + } + + return new MemberExpression(LeftNode, ".", RightNode); + case 'v': + Position += 2; + return ParseBinaryExpression("/"); + case 'V': + Position += 2; + return ParseBinaryExpression("/="); + } + return null; + case 'e': + switch (Peek(1)) + { + case 'o': + Position += 2; + return ParseBinaryExpression("^"); + case 'O': + Position += 2; + return ParseBinaryExpression("^="); + case 'q': + Position += 2; + return ParseBinaryExpression("=="); + } + return null; + case 'g': + switch (Peek(1)) + { + case 'e': + Position += 2; + return ParseBinaryExpression(">="); + case 't': + Position += 2; + return ParseBinaryExpression(">"); + } + return null; + case 'i': + switch (Peek(1)) + { + case 'x': + Position += 2; + BaseNode Base = ParseExpression(); + if (Base == null) + { + return null; + } + + BaseNode Subscript = ParseExpression(); + if (Base == null) + { + return null; + } + + return new ArraySubscriptingExpression(Base, Subscript); + case 'l': + Position += 2; + + List BracedExpressions = new List(); + while (!ConsumeIf("E")) + { + Expression = ParseBracedExpression(); + if (Expression == null) + { + return null; + } + + BracedExpressions.Add(Expression); + } + return new InitListExpression(null, BracedExpressions); + } + return null; + case 'l': + switch (Peek(1)) + { + case 'e': + Position += 2; + return ParseBinaryExpression("<="); + case 's': + Position += 2; + return ParseBinaryExpression("<<"); + case 'S': + Position += 2; + return ParseBinaryExpression("<<="); + case 't': + Position += 2; + return ParseBinaryExpression("<"); + } + return null; + case 'm': + switch (Peek(1)) + { + case 'i': + Position += 2; + return ParseBinaryExpression("-"); + case 'I': + Position += 2; + return ParseBinaryExpression("-="); + case 'l': + Position += 2; + return ParseBinaryExpression("*"); + case 'L': + Position += 2; + return ParseBinaryExpression("*="); + case 'm': + Position += 2; + if (ConsumeIf("_")) + { + return ParsePrefixExpression("--"); + } + + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new PostfixExpression(Expression, "--"); + } + return null; + case 'n': + switch (Peek(1)) + { + case 'a': + case 'w': + Position += 2; + return ParseNewExpression(); + case 'e': + Position += 2; + return ParseBinaryExpression("!="); + case 'g': + Position += 2; + return ParsePrefixExpression("-"); + case 't': + Position += 2; + return ParsePrefixExpression("!"); + case 'x': + Position += 2; + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new EnclosedExpression("noexcept (", Expression, ")"); + } + return null; + case 'o': + switch (Peek(1)) + { + case 'n': + return ParseUnresolvedName(); + case 'o': + Position += 2; + return ParseBinaryExpression("||"); + case 'r': + Position += 2; + return ParseBinaryExpression("|"); + case 'R': + Position += 2; + return ParseBinaryExpression("|="); + } + return null; + case 'p': + switch (Peek(1)) + { + case 'm': + Position += 2; + return ParseBinaryExpression("->*"); + case 'l': + case 's': + Position += 2; + return ParseBinaryExpression("+"); + case 'L': + Position += 2; + return ParseBinaryExpression("+="); + case 'p': + Position += 2; + if (ConsumeIf("_")) + { + return ParsePrefixExpression("++"); + } + + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new PostfixExpression(Expression, "++"); + case 't': + Position += 2; + LeftNode = ParseExpression(); + if (LeftNode == null) + { + return null; + } + + RightNode = ParseExpression(); + if (RightNode == null) + { + return null; + } + + return new MemberExpression(LeftNode, "->", RightNode); + } + return null; + case 'q': + if (Peek(1) == 'u') + { + Position += 2; + BaseNode Condition = ParseExpression(); + if (Condition == null) + { + return null; + } + + LeftNode = ParseExpression(); + if (LeftNode == null) + { + return null; + } + + RightNode = ParseExpression(); + if (RightNode == null) + { + return null; + } + + return new ConditionalExpression(Condition, LeftNode, RightNode); + } + return null; + case 'r': + switch (Peek(1)) + { + case 'c': + Position += 2; + BaseNode To = ParseType(); + if (To == null) + { + return null; + } + + BaseNode From = ParseExpression(); + if (From == null) + { + return null; + } + + return new CastExpression("reinterpret_cast", To, From); + case 'm': + Position += 2; + return ParseBinaryExpression("%"); + case 'M': + Position += 2; + return ParseBinaryExpression("%"); + case 's': + Position += 2; + return ParseBinaryExpression(">>"); + case 'S': + Position += 2; + return ParseBinaryExpression(">>="); + } + return null; + case 's': + switch (Peek(1)) + { + case 'c': + Position += 2; + BaseNode To = ParseType(); + if (To == null) + { + return null; + } + + BaseNode From = ParseExpression(); + if (From == null) + { + return null; + } + + return new CastExpression("static_cast", To, From); + case 'p': + Position += 2; + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new PackedTemplateParameterExpansion(Expression); + case 'r': + return ParseUnresolvedName(); + case 't': + Position += 2; + BaseNode EnclosedType = ParseType(); + if (EnclosedType == null) + { + return null; + } + + return new EnclosedExpression("sizeof (", EnclosedType, ")"); + case 'z': + Position += 2; + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new EnclosedExpression("sizeof (", Expression, ")"); + case 'Z': + Position += 2; + BaseNode SizeofParamNode = null; + switch (Peek()) + { + case 'T': + // FIXME: ??? Not entire sure if it's right + SizeofParamNode = ParseFunctionParameter(); + if (SizeofParamNode == null) + { + return null; + } + + return new EnclosedExpression("sizeof...(", new PackedTemplateParameterExpansion(SizeofParamNode), ")"); + case 'f': + SizeofParamNode = ParseFunctionParameter(); + if (SizeofParamNode == null) + { + return null; + } + + return new EnclosedExpression("sizeof...(", SizeofParamNode, ")"); + } + return null; + case 'P': + Position += 2; + List Arguments = new List(); + while (!ConsumeIf("E")) + { + BaseNode Argument = ParseTemplateArgument(); + if (Argument == null) + { + return null; + } + + Arguments.Add(Argument); + } + return new EnclosedExpression("sizeof...(", new NodeArray(Arguments), ")"); + } + return null; + case 't': + switch (Peek(1)) + { + case 'e': + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new EnclosedExpression("typeid (", Expression, ")"); + case 't': + BaseNode EnclosedType = ParseExpression(); + if (EnclosedType == null) + { + return null; + } + + return new EnclosedExpression("typeid (", EnclosedType, ")"); + case 'l': + Position += 2; + BaseNode TypeNode = ParseType(); + if (TypeNode == null) + { + return null; + } + + List BracedExpressions = new List(); + while (!ConsumeIf("E")) + { + Expression = ParseBracedExpression(); + if (Expression == null) + { + return null; + } + + BracedExpressions.Add(Expression); + } + return new InitListExpression(TypeNode, BracedExpressions); + case 'r': + Position += 2; + return new NameType("throw"); + case 'w': + Position += 2; + Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + return new ThrowExpression(Expression); + } + return null; + } + + if (char.IsDigit(Peek())) + { + return ParseUnresolvedName(); + } + + return null; + } + + private BaseNode ParseIntegerLiteral(string LiteralName) + { + string Number = ParseNumber(true); + if (Number == null || Number.Length == 0 || !ConsumeIf("E")) + { + return null; + } + + return new IntegerLiteral(LiteralName, Number); + } + + // ::= L E # integer literal + // ::= L E # floating literal (TODO) + // ::= L E # string literal + // ::= L E # nullptr literal (i.e., "LDnE") + // ::= L 0 E # null pointer template argument + // ::= L _ E # complex floating point literal (C 2000) + // ::= L _Z E # external name + private BaseNode ParseExpressionPrimary() + { + if (!ConsumeIf("L")) + { + return null; + } + + switch (Peek()) + { + case 'w': + Position++; + return ParseIntegerLiteral("wchar_t"); + case 'b': + if (ConsumeIf("b0E")) + { + return new NameType("false", NodeType.BooleanExpression); + } + + if (ConsumeIf("b1E")) + { + return new NameType("true", NodeType.BooleanExpression); + } + + return null; + case 'c': + Position++; + return ParseIntegerLiteral("char"); + case 'a': + Position++; + return ParseIntegerLiteral("signed char"); + case 'h': + Position++; + return ParseIntegerLiteral("unsigned char"); + case 's': + Position++; + return ParseIntegerLiteral("short"); + case 't': + Position++; + return ParseIntegerLiteral("unsigned short"); + case 'i': + Position++; + return ParseIntegerLiteral(""); + case 'j': + Position++; + return ParseIntegerLiteral("u"); + case 'l': + Position++; + return ParseIntegerLiteral("l"); + case 'm': + Position++; + return ParseIntegerLiteral("ul"); + case 'x': + Position++; + return ParseIntegerLiteral("ll"); + case 'y': + Position++; + return ParseIntegerLiteral("ull"); + case 'n': + Position++; + return ParseIntegerLiteral("__int128"); + case 'o': + Position++; + return ParseIntegerLiteral("unsigned __int128"); + case 'd': + case 'e': + case 'f': + // TODO: floating literal + return null; + case '_': + if (ConsumeIf("_Z")) + { + BaseNode Encoding = ParseEncoding(); + if (Encoding != null && ConsumeIf("E")) + { + return Encoding; + } + } + return null; + case 'T': + return null; + default: + BaseNode Type = ParseType(); + if (Type == null) + { + return null; + } + + string Number = ParseNumber(); + if (Number == null || Number.Length == 0 || !ConsumeIf("E")) + { + return null; + } + + return new IntegerCastExpression(Type, Number); + } + } + + // ::= Dt E # decltype of an id-expression or class member access (C++0x) + // ::= DT E # decltype of an expression (C++0x) + private BaseNode ParseDecltype() + { + if (!ConsumeIf("D") || (!ConsumeIf("t") && !ConsumeIf("T"))) + { + return null; + } + + BaseNode Expression = ParseExpression(); + if (Expression == null) + { + return null; + } + + if (!ConsumeIf("E")) + { + return null; + } + + return new EnclosedExpression("decltype(", Expression, ")"); + } + + // ::= T_ # first template parameter + // ::= T _ + // ::= + // ::= + private BaseNode ParseTemplateParam() + { + if (!ConsumeIf("T")) + { + return null; + } + + int Index = 0; + if (!ConsumeIf("_")) + { + Index = ParsePositiveNumber(); + if (Index < 0) + { + return null; + } + + Index++; + if (!ConsumeIf("_")) + { + return null; + } + } + + // 5.1.8: TODO: lambda? + // if (IsParsingLambdaParameters) + // return new NameType("auto"); + + if (CanForwardTemplateReference) + { + ForwardTemplateReference ForwardTemplateReference = new ForwardTemplateReference(Index); + ForwardTemplateReferenceList.Add(ForwardTemplateReference); + return ForwardTemplateReference; + } + if (Index >= TemplateParamList.Count) + { + return null; + } + + return TemplateParamList[Index]; + } + + // ::= I + E + private BaseNode ParseTemplateArguments(bool HasContext = false) + { + if (!ConsumeIf("I")) + { + return null; + } + + if (HasContext) + { + TemplateParamList.Clear(); + } + + List Args = new List(); + while (!ConsumeIf("E")) + { + if (HasContext) + { + List TemplateParamListTemp = new List(TemplateParamList); + BaseNode TemplateArgument = ParseTemplateArgument(); + TemplateParamList = TemplateParamListTemp; + if (TemplateArgument == null) + { + return null; + } + + Args.Add(TemplateArgument); + if (TemplateArgument.GetType().Equals(NodeType.PackedTemplateArgument)) + { + TemplateArgument = new PackedTemplateParameter(((NodeArray)TemplateArgument).Nodes); + } + TemplateParamList.Add(TemplateArgument); + } + else + { + BaseNode TemplateArgument = ParseTemplateArgument(); + if (TemplateArgument == null) + { + return null; + } + + Args.Add(TemplateArgument); + } + } + return new TemplateArguments(Args); + } + + + // ::= # type or template + // ::= X E # expression + // ::= # simple expressions + // ::= J * E # argument pack + private BaseNode ParseTemplateArgument() + { + switch (Peek()) + { + // X E + case 'X': + Position++; + BaseNode Expression = ParseExpression(); + if (Expression == null || !ConsumeIf("E")) + { + return null; + } + + return Expression; + // + case 'L': + return ParseExpressionPrimary(); + // J * E + case 'J': + Position++; + List TemplateArguments = new List(); + while (!ConsumeIf("E")) + { + BaseNode TemplateArgument = ParseTemplateArgument(); + if (TemplateArgument == null) + { + return null; + } + + TemplateArguments.Add(TemplateArgument); + } + return new NodeArray(TemplateArguments, NodeType.PackedTemplateArgument); + // + default: + return ParseType(); + } + } + + class NameParserContext + { + public CVType CV; + public SimpleReferenceType Ref; + public bool FinishWithTemplateArguments; + public bool CtorDtorConversion; + } + + + // ::= [ ] # T:: or T:: + // ::= # decltype(p):: + // ::= + private BaseNode ParseUnresolvedType() + { + if (Peek() == 'T') + { + BaseNode TemplateParam = ParseTemplateParam(); + if (TemplateParam == null) + { + return null; + } + + SubstitutionList.Add(TemplateParam); + return TemplateParam; + } + else if (Peek() == 'D') + { + BaseNode DeclType = ParseDecltype(); + if (DeclType == null) + { + return null; + } + + SubstitutionList.Add(DeclType); + return DeclType; + } + return ParseSubstitution(); + } + + // ::= [ ] + private BaseNode ParseSimpleId() + { + BaseNode SourceName = ParseSourceName(); + if (SourceName == null) + { + return null; + } + + if (Peek() == 'I') + { + BaseNode TemplateArguments = ParseTemplateArguments(); + if (TemplateArguments == null) + { + return null; + } + + return new NameTypeWithTemplateArguments(SourceName, TemplateArguments); + } + return SourceName; + } + + // ::= # e.g., ~T or ~decltype(f()) + // ::= # e.g., ~A<2*N> + private BaseNode ParseDestructorName() + { + BaseNode Node; + if (char.IsDigit(Peek())) + { + Node = ParseSimpleId(); + } + else + { + Node = ParseUnresolvedType(); + } + if (Node == null) + { + return null; + } + + return new DtorName(Node); + } + + // ::= # unresolved name + // extension ::= # unresolved operator-function-id + // extension ::= # unresolved operator template-id + // ::= on # unresolved operator-function-id + // ::= on # unresolved operator template-id + // ::= dn # destructor or pseudo-destructor; + // # e.g. ~X or ~X + private BaseNode ParseBaseUnresolvedName() + { + if (char.IsDigit(Peek())) + { + return ParseSimpleId(); + } + else if (ConsumeIf("dn")) + { + return ParseDestructorName(); + } + + ConsumeIf("on"); + BaseNode OperatorName = ParseOperatorName(null); + if (OperatorName == null) + { + return null; + } + + if (Peek() == 'I') + { + BaseNode TemplateArguments = ParseTemplateArguments(); + if (TemplateArguments == null) + { + return null; + } + + return new NameTypeWithTemplateArguments(OperatorName, TemplateArguments); + } + return OperatorName; + } + + // ::= [gs] # x or (with "gs") ::x + // ::= sr # T::x / decltype(p)::x + // ::= srN + E + // # T::N::x /decltype(p)::N::x + // ::= [gs] sr + E + // # A::x, N::y, A::z; "gs" means leading "::" + private BaseNode ParseUnresolvedName(NameParserContext Context = null) + { + BaseNode Result = null; + if (ConsumeIf("srN")) + { + Result = ParseUnresolvedType(); + if (Result == null) + { + return null; + } + + if (Peek() == 'I') + { + BaseNode TemplateArguments = ParseTemplateArguments(); + if (TemplateArguments == null) + { + return null; + } + + Result = new NameTypeWithTemplateArguments(Result, TemplateArguments); + if (Result == null) + { + return null; + } + } + + while (!ConsumeIf("E")) + { + BaseNode SimpleId = ParseSimpleId(); + if (SimpleId == null) + { + return null; + } + + Result = new QualifiedName(Result, SimpleId); + if (Result == null) + { + return null; + } + } + + BaseNode BaseName = ParseBaseUnresolvedName(); + if (BaseName == null) + { + return null; + } + + return new QualifiedName(Result, BaseName); + } + + bool IsGlobal = ConsumeIf("gs"); + + // ::= [gs] # x or (with "gs") ::x + if (!ConsumeIf("sr")) + { + Result = ParseBaseUnresolvedName(); + if (Result == null) + { + return null; + } + + if (IsGlobal) + { + Result = new GlobalQualifiedName(Result); + } + + return Result; + } + + // ::= [gs] sr + E + if (char.IsDigit(Peek())) + { + do + { + BaseNode Qualifier = ParseSimpleId(); + if (Qualifier == null) + { + return null; + } + + if (Result != null) + { + Result = new QualifiedName(Result, Qualifier); + } + else if (IsGlobal) + { + Result = new GlobalQualifiedName(Qualifier); + } + else + { + Result = Qualifier; + } + + if (Result == null) + { + return null; + } + } while (!ConsumeIf("E")); + } + // ::= sr [tempate-args] # T::x / decltype(p)::x + else + { + Result = ParseUnresolvedType(); + if (Result == null) + { + return null; + } + + if (Peek() == 'I') + { + BaseNode TemplateArguments = ParseTemplateArguments(); + if (TemplateArguments == null) + { + return null; + } + + Result = new NameTypeWithTemplateArguments(Result, TemplateArguments); + if (Result == null) + { + return null; + } + } + } + + if (Result == null) + { + return null; + } + + BaseNode BaseUnresolvedName = ParseBaseUnresolvedName(); + if (BaseUnresolvedName == null) + { + return null; + } + + return new QualifiedName(Result, BaseUnresolvedName); + } + + // ::= + // ::= St # ::std:: + private BaseNode ParseUnscopedName(NameParserContext Context) + { + if (ConsumeIf("St")) + { + BaseNode UnresolvedName = ParseUnresolvedName(Context); + if (UnresolvedName == null) + { + return null; + } + + return new StdQualifiedName(UnresolvedName); + } + return ParseUnresolvedName(Context); + } + + // ::= N [] [] E + // ::= N [] [] E + private BaseNode ParseNestedName(NameParserContext Context) + { + // Impossible in theory + if (Consume() != 'N') + { + return null; + } + + BaseNode Result = null; + CVType CV = new CVType(ParseCVQualifiers(), null); + if (Context != null) + { + Context.CV = CV; + } + + SimpleReferenceType Ref = ParseRefQualifiers(); + if (Context != null) + { + Context.Ref = Ref; + } + + if (ConsumeIf("St")) + { + Result = new NameType("std"); + } + + while (!ConsumeIf("E")) + { + // end + if (ConsumeIf("M")) + { + if (Result == null) + { + return null; + } + + continue; + } + char C = Peek(); + + // TODO: template args + if (C == 'T') + { + BaseNode TemplateParam = ParseTemplateParam(); + if (TemplateParam == null) + { + return null; + } + + Result = CreateNameNode(Result, TemplateParam, Context); + SubstitutionList.Add(Result); + continue; + } + + // + if (C == 'I') + { + BaseNode TemplateArgument = ParseTemplateArguments(Context != null); + if (TemplateArgument == null || Result == null) + { + return null; + } + + Result = new NameTypeWithTemplateArguments(Result, TemplateArgument); + if (Context != null) + { + Context.FinishWithTemplateArguments = true; + } + + SubstitutionList.Add(Result); + continue; + } + + // + if (C == 'D' && (Peek(1) == 't' || Peek(1) == 'T')) + { + BaseNode Decltype = ParseDecltype(); + if (Decltype == null) + { + return null; + } + + Result = CreateNameNode(Result, Decltype, Context); + SubstitutionList.Add(Result); + continue; + } + + // + if (C == 'S' && Peek(1) != 't') + { + BaseNode Substitution = ParseSubstitution(); + if (Substitution == null) + { + return null; + } + + Result = CreateNameNode(Result, Substitution, Context); + if (Result != Substitution) + { + SubstitutionList.Add(Substitution); + } + + continue; + } + + // of ParseUnqualifiedName + if (C == 'C' || (C == 'D' && Peek(1) != 'C')) + { + // We cannot have nothing before this + if (Result == null) + { + return null; + } + + BaseNode CtOrDtorName = ParseCtorDtorName(Context, Result); + + if (CtOrDtorName == null) + { + return null; + } + + Result = CreateNameNode(Result, CtOrDtorName, Context); + + // TODO: ABI Tags (before) + if (Result == null) + { + return null; + } + + SubstitutionList.Add(Result); + continue; + } + + BaseNode UnqualifiedName = ParseUnqualifiedName(Context); + if (UnqualifiedName == null) + { + return null; + } + Result = CreateNameNode(Result, UnqualifiedName, Context); + + SubstitutionList.Add(Result); + } + if (Result == null || SubstitutionList.Count == 0) + { + return null; + } + + SubstitutionList.RemoveAt(SubstitutionList.Count - 1); + return Result; + } + + // ::= _ # when number < 10 + // ::= __ _ # when number >= 10 + private void ParseDiscriminator() + { + if (Count() == 0) + { + return; + } + // We ignore the discriminator, we don't need it. + if (ConsumeIf("_")) + { + ConsumeIf("_"); + while (char.IsDigit(Peek()) && Count() != 0) + { + Consume(); + } + ConsumeIf("_"); + } + } + + // ::= Z E [] + // ::= Z E s [] + // ::= Z Ed [ ] _ + private BaseNode ParseLocalName(NameParserContext Context) + { + if (!ConsumeIf("Z")) + { + return null; + } + + BaseNode Encoding = ParseEncoding(); + if (Encoding == null || !ConsumeIf("E")) + { + return null; + } + + BaseNode EntityName; + if (ConsumeIf("s")) + { + ParseDiscriminator(); + return new LocalName(Encoding, new NameType("string literal")); + } + else if (ConsumeIf("d")) + { + ParseNumber(true); + if (!ConsumeIf("_")) + { + return null; + } + + EntityName = ParseName(Context); + if (EntityName == null) + { + return null; + } + + return new LocalName(Encoding, EntityName); + } + + EntityName = ParseName(Context); + if (EntityName == null) + { + return null; + } + + ParseDiscriminator(); + return new LocalName(Encoding, EntityName); + } + + // ::= + // ::= + // ::= + // ::= # See Scope Encoding below (TODO) + private BaseNode ParseName(NameParserContext Context = null) + { + ConsumeIf("L"); + + if (Peek() == 'N') + { + return ParseNestedName(Context); + } + + if (Peek() == 'Z') + { + return ParseLocalName(Context); + } + + if (Peek() == 'S' && Peek(1) != 't') + { + BaseNode Substitution = ParseSubstitution(); + if (Substitution == null) + { + return null; + } + + if (Peek() != 'I') + { + return null; + } + + BaseNode TemplateArguments = ParseTemplateArguments(Context != null); + if (TemplateArguments == null) + { + return null; + } + + if (Context != null) + { + Context.FinishWithTemplateArguments = true; + } + + return new NameTypeWithTemplateArguments(Substitution, TemplateArguments); + } + + BaseNode Result = ParseUnscopedName(Context); + if (Result == null) + { + return null; + } + + if (Peek() == 'I') + { + SubstitutionList.Add(Result); + BaseNode TemplateArguments = ParseTemplateArguments(Context != null); + if (TemplateArguments == null) + { + return null; + } + + if (Context != null) + { + Context.FinishWithTemplateArguments = true; + } + + return new NameTypeWithTemplateArguments(Result, TemplateArguments); + } + + return Result; + } + + private bool IsEncodingEnd() + { + char C = Peek(); + return Count() == 0 || C == 'E' || C == '.' || C == '_'; + } + + // ::= + // ::= + // ::= + private BaseNode ParseEncoding() + { + NameParserContext Context = new NameParserContext(); + if (Peek() == 'T' || (Peek() == 'G' && Peek(1) == 'V')) + { + return ParseSpecialName(Context); + } + + BaseNode Name = ParseName(Context); + if (Name == null) + { + return null; + } + + // TODO: compute template refs here + + if (IsEncodingEnd()) + { + return Name; + } + + // TODO: Ua9enable_ifI + + BaseNode ReturnType = null; + if (!Context.CtorDtorConversion && Context.FinishWithTemplateArguments) + { + ReturnType = ParseType(); + if (ReturnType == null) + { + return null; + } + } + + if (ConsumeIf("v")) + { + return new EncodedFunction(Name, null, Context.CV, Context.Ref, null, ReturnType); + } + + List Params = new List(); + + // backup because that can be destroyed by parseType + CVType CV = Context.CV; + SimpleReferenceType Ref = Context.Ref; + + while (!IsEncodingEnd()) + { + BaseNode Param = ParseType(); + if (Param == null) + { + return null; + } + + Params.Add(Param); + } + + return new EncodedFunction(Name, new NodeArray(Params), CV, Ref, null, ReturnType); + } + + // ::= _Z + // ::= + private BaseNode Parse() + { + if (ConsumeIf("_Z")) + { + BaseNode Encoding = ParseEncoding(); + if (Encoding != null && Count() == 0) + { + return Encoding; + } + return null; + } + else + { + BaseNode Type = ParseType(); + if (Type != null && Count() == 0) + { + return Type; + } + return null; + } + } + + public static string Parse(string OriginalMangled) + { + Demangler Instance = new Demangler(OriginalMangled); + BaseNode ResNode = Instance.Parse(); + + if (ResNode != null) + { + StringWriter Writer = new StringWriter(); + ResNode.Print(Writer); + return Writer.ToString(); + } + + return OriginalMangled; + } + } +} diff --git a/Ryujinx.HLE/HOS/Homebrew.cs b/Ryujinx.HLE/HOS/Homebrew.cs index 1f862a4ae8..ffb22c7f5c 100644 --- a/Ryujinx.HLE/HOS/Homebrew.cs +++ b/Ryujinx.HLE/HOS/Homebrew.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS public const string TemporaryNroSuffix = ".ryu_tmp.nro"; //http://switchbrew.org/index.php?title=Homebrew_ABI - public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath) + public static void WriteHbAbiData(MemoryManager Memory, long Position, int MainThreadHandle, string SwitchPath) { //MainThreadHandle. WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle); @@ -31,12 +31,12 @@ namespace Ryujinx.HLE.HOS } private static void WriteConfigEntry( - AMemory Memory, - ref long Position, - int Key, - int Flags = 0, - long Value0 = 0, - long Value1 = 0) + MemoryManager Memory, + ref long Position, + int Key, + int Flags = 0, + long Value0 = 0, + long Value1 = 0) { Memory.WriteInt32(Position + 0x00, Key); Memory.WriteInt32(Position + 0x04, Flags); @@ -46,7 +46,7 @@ namespace Ryujinx.HLE.HOS Position += 0x18; } - public static string ReadHbAbiNextLoadPath(AMemory Memory, long Position) + public static string ReadHbAbiNextLoadPath(MemoryManager Memory, long Position) { string FileName = null; @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS long Value0 = Memory.ReadInt64(Position + 0x08); long Value1 = Memory.ReadInt64(Position + 0x10); - FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, Value1 - Value0); + FileName = MemoryHelper.ReadAsciiString(Memory, Value0, Value1 - Value0); break; } diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 2e216cdf18..1b336647fa 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -1,14 +1,16 @@ using LibHac; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Font; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; -using Ryujinx.HLE.Logging; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Linq; +using Nso = Ryujinx.HLE.Loaders.Executables.Nso; namespace Ryujinx.HLE.HOS { @@ -19,12 +21,22 @@ namespace Ryujinx.HLE.HOS private Switch Device; - private KProcessScheduler Scheduler; - private ConcurrentDictionary Processes; public SystemStateMgr State { get; private set; } + internal KRecursiveLock CriticalSectionLock { get; private set; } + + internal KScheduler Scheduler { get; private set; } + + internal KTimeManager TimeManager { get; private set; } + + internal KAddressArbiter AddressArbiter { get; private set; } + + internal KSynchronization Synchronization { get; private set; } + + internal LinkedList Withholders { get; private set; } + internal KSharedMemory HidSharedMem { get; private set; } internal KSharedMemory FontSharedMem { get; private set; } @@ -34,16 +46,36 @@ namespace Ryujinx.HLE.HOS internal Keyset KeySet { get; private set; } + private bool HasStarted; + + public Nacp ControlData { get; set; } + + public string CurrentTitle { get; private set; } + + public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; } + public Horizon(Switch Device) { this.Device = Device; - Scheduler = new KProcessScheduler(Device.Log); - Processes = new ConcurrentDictionary(); State = new SystemStateMgr(); + CriticalSectionLock = new KRecursiveLock(this); + + Scheduler = new KScheduler(this); + + TimeManager = new KTimeManager(); + + AddressArbiter = new KAddressArbiter(this); + + Synchronization = new KSynchronization(this); + + Withholders = new LinkedList(); + + Scheduler.StartAutoPreemptionThread(); + if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) || !Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA)) { @@ -55,7 +87,7 @@ namespace Ryujinx.HLE.HOS Font = new SharedFontManager(Device, FontSharedMem.PA); - VsyncEvent = new KEvent(); + VsyncEvent = new KEvent(this); LoadKeySet(); } @@ -73,7 +105,7 @@ namespace Ryujinx.HLE.HOS if (File.Exists(NpdmFileName)) { - Device.Log.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); + Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open)) { @@ -82,7 +114,7 @@ namespace Ryujinx.HLE.HOS } else { - Device.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); } Process MainProcess = MakeProcess(MetaData); @@ -96,7 +128,7 @@ namespace Ryujinx.HLE.HOS continue; } - Device.Log.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}..."); + Logger.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}..."); using (FileStream Input = new FileStream(File, FileMode.Open)) { @@ -114,6 +146,8 @@ namespace Ryujinx.HLE.HOS throw new NotImplementedException("32-bit titles are unsupported!"); } + CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16"); + LoadNso("rtld"); MainProcess.SetEmptyArgs(); @@ -131,27 +165,38 @@ namespace Ryujinx.HLE.HOS Xci Xci = new Xci(KeySet, File); - Nca Nca = GetXciMainNca(Xci); + (Nca MainNca, Nca ControlNca) = GetXciGameData(Xci); - if (Nca == null) + if (MainNca == null) { - Device.Log.PrintError(LogClass.Loader, "Unable to load XCI"); + Logger.PrintError(LogClass.Loader, "Unable to load XCI"); return; } - LoadNca(Nca); + LoadNca(MainNca, ControlNca); } - private Nca GetXciMainNca(Xci Xci) + private (Nca Main, Nca Control) GetXciGameData(Xci Xci) { if (Xci.SecurePartition == null) { throw new InvalidDataException("Could not find XCI secure partition"); } - Nca MainNca = null; - Nca PatchNca = null; + Nca MainNca = null; + Nca PatchNca = null; + Nca ControlNca = null; + + foreach (PfsFileEntry TicketEntry in Xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".tik"))) + { + Ticket ticket = new Ticket(Xci.SecurePartition.OpenFile(TicketEntry)); + + if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId)) + { + KeySet.TitleKeys.Add(ticket.RightsId, ticket.GetTitleKey(KeySet)); + } + } foreach (PfsFileEntry FileEntry in Xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".nca"))) { @@ -170,16 +215,43 @@ namespace Ryujinx.HLE.HOS PatchNca = Nca; } } + else if (Nca.Header.ContentType == ContentType.Control) + { + ControlNca = Nca; + } } if (MainNca == null) { - Device.Log.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file"); + Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file"); } MainNca.SetBaseNca(PatchNca); - return MainNca; + if (ControlNca != null) + { + ReadControlData(ControlNca); + } + + if (PatchNca != null) + { + PatchNca.SetBaseNca(MainNca); + + return (PatchNca, ControlNca); + } + + return (MainNca, ControlNca); + } + + public void ReadControlData(Nca ControlNca) + { + Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, FsIntegrityCheckLevel)); + + byte[] ControlFile = ControlRomfs.GetFile("/control.nacp"); + + BinaryReader Reader = new BinaryReader(new MemoryStream(ControlFile)); + + ControlData = new Nacp(Reader); } public void LoadNca(string NcaFile) @@ -188,7 +260,7 @@ namespace Ryujinx.HLE.HOS Nca Nca = new Nca(KeySet, File, true); - LoadNca(Nca); + LoadNca(Nca, null); } public void LoadNsp(string NspFile) @@ -202,63 +274,79 @@ namespace Ryujinx.HLE.HOS // Load title key from the NSP's ticket in case the user doesn't have a title key file if (TicketFile != null) { - // todo Change when Ticket(Stream) overload is added - Ticket Ticket = new Ticket(new BinaryReader(Nsp.OpenFile(TicketFile))); + Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile)); KeySet.TitleKeys[Ticket.RightsId] = Ticket.GetTitleKey(KeySet); } + Nca MainNca = null; + Nca ControlNca = null; + foreach (PfsFileEntry NcaFile in Nsp.Files.Where(x => x.Name.EndsWith(".nca"))) { Nca Nca = new Nca(KeySet, Nsp.OpenFile(NcaFile), true); if (Nca.Header.ContentType == ContentType.Program) { - LoadNca(Nca); - - return; + MainNca = Nca; + } + else if (Nca.Header.ContentType == ContentType.Control) + { + ControlNca = Nca; } } - Device.Log.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file"); + if (MainNca != null) + { + LoadNca(MainNca, ControlNca); + + return; + } + + Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file"); } - public void LoadNca(Nca Nca) + public void LoadNca(Nca MainNca, Nca ControlNca) { - NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); - NcaSection ExefsSection = Nca.Sections.FirstOrDefault(x => x?.IsExefs == true); - - if (ExefsSection == null) + if (MainNca.Header.ContentType != ContentType.Program) { - Device.Log.PrintError(LogClass.Loader, "No ExeFS found in NCA"); + Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA"); return; } - if (RomfsSection == null) + Stream RomfsStream = MainNca.OpenSection(ProgramPartitionType.Data, false, FsIntegrityCheckLevel); + Stream ExefsStream = MainNca.OpenSection(ProgramPartitionType.Code, false, FsIntegrityCheckLevel); + + if (ExefsStream == null) { - Device.Log.PrintError(LogClass.Loader, "No RomFS found in NCA"); + Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA"); return; } - Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false); - Device.FileSystem.SetRomFs(RomfsStream); + if (RomfsStream == null) + { + Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA"); + } + else + { + Device.FileSystem.SetRomFs(RomfsStream); + } - Stream ExefsStream = Nca.OpenSection(ExefsSection.SectionNum, false); Pfs Exefs = new Pfs(ExefsStream); Npdm MetaData = null; if (Exefs.FileExists("main.npdm")) { - Device.Log.PrintInfo(LogClass.Loader, "Loading main.npdm..."); + Logger.PrintInfo(LogClass.Loader, "Loading main.npdm..."); MetaData = new Npdm(Exefs.OpenFile("main.npdm")); } else { - Device.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); } Process MainProcess = MakeProcess(MetaData); @@ -272,7 +360,7 @@ namespace Ryujinx.HLE.HOS continue; } - Device.Log.PrintInfo(LogClass.Loader, $"Loading {Filename}..."); + Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}..."); string Name = Path.GetFileNameWithoutExtension(File.Name); @@ -282,6 +370,35 @@ namespace Ryujinx.HLE.HOS } } + Nacp ReadControlData() + { + Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, FsIntegrityCheckLevel)); + + byte[] ControlFile = ControlRomfs.GetFile("/control.nacp"); + + BinaryReader Reader = new BinaryReader(new MemoryStream(ControlFile)); + + Nacp ControlData = new Nacp(Reader); + + CurrentTitle = ControlData.Languages[(int)State.DesiredTitleLanguage].Title; + + if (string.IsNullOrWhiteSpace(CurrentTitle)) + { + CurrentTitle = ControlData.Languages.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title; + } + + return ControlData; + } + + if (ControlNca != null) + { + MainProcess.ControlData = ReadControlData(); + } + else + { + CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16"); + } + if (!MainProcess.MetaData.Is64Bits) { throw new NotImplementedException("32-bit titles are unsupported!"); @@ -371,10 +488,15 @@ namespace Ryujinx.HLE.HOS } } - public void SignalVsync() => VsyncEvent.WaitEvent.Set(); + public void SignalVsync() + { + VsyncEvent.ReadableEvent.Signal(); + } private Process MakeProcess(Npdm MetaData = null) { + HasStarted = true; + Process Process; lock (Processes) @@ -386,7 +508,7 @@ namespace Ryujinx.HLE.HOS ProcessId++; } - Process = new Process(Device, Scheduler, ProcessId, MetaData); + Process = new Process(Device, ProcessId, MetaData); Processes.TryAdd(ProcessId, Process); } @@ -409,18 +531,29 @@ namespace Ryujinx.HLE.HOS if (Processes.Count == 0) { - Unload(); + Scheduler.Dispose(); + + TimeManager.Dispose(); Device.Unload(); } } } - private void Unload() + public void EnableMultiCoreScheduling() { - VsyncEvent.Dispose(); + if (!HasStarted) + { + Scheduler.MultiCoreScheduling = true; + } + } - Scheduler.Dispose(); + public void DisableMultiCoreScheduling() + { + if (!HasStarted) + { + Scheduler.MultiCoreScheduling = false; + } } public void Dispose() diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs index 08a4cdb5d0..ec27a6eaf9 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs @@ -8,12 +8,12 @@ namespace Ryujinx.HLE.HOS.Ipc static class IpcHandler { public static long IpcCall( - Switch Ns, - Process Process, - AMemory Memory, - KSession Session, - IpcMessage Request, - long CmdPtr) + Switch Ns, + Process Process, + MemoryManager Memory, + KSession Session, + IpcMessage Request, + long CmdPtr) { IpcMessage Response = new IpcMessage(); @@ -73,7 +73,10 @@ namespace Ryujinx.HLE.HOS.Ipc { int Unknown = ReqReader.ReadInt32(); - int Handle = Process.HandleTable.OpenHandle(Session); + if (Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); diff --git a/Ryujinx.HLE/HOS/Kernel/AddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/AddressArbiter.cs deleted file mode 100644 index d7df0a727d..0000000000 --- a/Ryujinx.HLE/HOS/Kernel/AddressArbiter.cs +++ /dev/null @@ -1,111 +0,0 @@ -using ChocolArm64.Memory; -using ChocolArm64.State; - -using static Ryujinx.HLE.HOS.ErrorCode; - -namespace Ryujinx.HLE.HOS.Kernel -{ - static class AddressArbiter - { - static ulong WaitForAddress(Process Process, AThreadState ThreadState, long Address, ulong Timeout) - { - KThread CurrentThread = Process.GetThread(ThreadState.Tpidr); - - Process.Scheduler.SetReschedule(CurrentThread.ProcessorId); - - CurrentThread.ArbiterWaitAddress = Address; - CurrentThread.ArbiterSignaled = false; - - Process.Scheduler.EnterWait(CurrentThread, NsTimeConverter.GetTimeMs(Timeout)); - - if (!CurrentThread.ArbiterSignaled) - { - return MakeError(ErrorModule.Kernel, KernelErr.Timeout); - } - - return 0; - } - - public static ulong WaitForAddressIfLessThan(Process Process, - AThreadState ThreadState, - AMemory Memory, - long Address, - int Value, - ulong Timeout, - bool ShouldDecrement) - { - Memory.SetExclusive(ThreadState, Address); - - int CurrentValue = Memory.ReadInt32(Address); - - while (true) - { - if (Memory.TestExclusive(ThreadState, Address)) - { - if (CurrentValue < Value) - { - if (ShouldDecrement) - { - Memory.WriteInt32(Address, CurrentValue - 1); - } - - Memory.ClearExclusiveForStore(ThreadState); - } - else - { - Memory.ClearExclusiveForStore(ThreadState); - - return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); - } - - break; - } - - Memory.SetExclusive(ThreadState, Address); - - CurrentValue = Memory.ReadInt32(Address); - } - - if (Timeout == 0) - { - return MakeError(ErrorModule.Kernel, KernelErr.Timeout); - } - - return WaitForAddress(Process, ThreadState, Address, Timeout); - } - - public static ulong WaitForAddressIfEqual(Process Process, - AThreadState ThreadState, - AMemory Memory, - long Address, - int Value, - ulong Timeout) - { - if (Memory.ReadInt32(Address) != Value) - { - return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); - } - - if (Timeout == 0) - { - return MakeError(ErrorModule.Kernel, KernelErr.Timeout); - } - - return WaitForAddress(Process, ThreadState, Address, Timeout); - } - } - - enum ArbitrationType : int - { - WaitIfLessThan, - DecrementAndWaitIfLessThan, - WaitIfEqual - } - - enum SignalType : int - { - Signal, - IncrementAndSignalIfEqual, - ModifyByWaitingCountAndSignalIfEqual - } -} diff --git a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs new file mode 100644 index 0000000000..8a2d47f7b0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum ArbitrationType + { + WaitIfLessThan = 0, + DecrementAndWaitIfLessThan = 1, + WaitIfEqual = 2 + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs new file mode 100644 index 0000000000..0bfa2710cd --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs @@ -0,0 +1,29 @@ +using System.Collections.Concurrent; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class HleCoreManager + { + private ConcurrentDictionary Threads; + + public HleCoreManager() + { + Threads = new ConcurrentDictionary(); + } + + public ManualResetEvent GetThread(Thread Thread) + { + return Threads.GetOrAdd(Thread, (Key) => new ManualResetEvent(false)); + } + + public void RemoveThread(Thread Thread) + { + if (Threads.TryRemove(Thread, out ManualResetEvent Event)) + { + Event.Set(); + Event.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs new file mode 100644 index 0000000000..e0cb158c98 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs @@ -0,0 +1,149 @@ +using System; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel +{ + partial class KScheduler + { + private const int RoundRobinTimeQuantumMs = 10; + + private int CurrentCore; + + public bool MultiCoreScheduling { get; set; } + + private HleCoreManager CoreManager; + + private bool KeepPreempting; + + public void StartAutoPreemptionThread() + { + Thread PreemptionThread = new Thread(PreemptCurrentThread); + + KeepPreempting = true; + + PreemptionThread.Start(); + } + + public void ContextSwitch() + { + lock (CoreContexts) + { + if (MultiCoreScheduling) + { + int SelectedCount = 0; + + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + KCoreContext CoreContext = CoreContexts[Core]; + + if (CoreContext.ContextSwitchNeeded && (CoreContext.CurrentThread?.Context.IsCurrentThread() ?? false)) + { + CoreContext.ContextSwitch(); + } + + if (CoreContext.CurrentThread?.Context.IsCurrentThread() ?? false) + { + SelectedCount++; + } + } + + if (SelectedCount == 0) + { + CoreManager.GetThread(Thread.CurrentThread).Reset(); + } + else if (SelectedCount == 1) + { + CoreManager.GetThread(Thread.CurrentThread).Set(); + } + else + { + throw new InvalidOperationException("Thread scheduled in more than one core!"); + } + } + else + { + KThread CurrentThread = CoreContexts[CurrentCore].CurrentThread; + + bool HasThreadExecuting = CurrentThread != null; + + if (HasThreadExecuting) + { + //If this is not the thread that is currently executing, we need + //to request an interrupt to allow safely starting another thread. + if (!CurrentThread.Context.IsCurrentThread()) + { + CurrentThread.Context.RequestInterrupt(); + + return; + } + + CoreManager.GetThread(CurrentThread.Context.Work).Reset(); + } + + //Advance current core and try picking a thread, + //keep advancing if it is null. + for (int Core = 0; Core < 4; Core++) + { + CurrentCore = (CurrentCore + 1) % CpuCoresCount; + + KCoreContext CoreContext = CoreContexts[CurrentCore]; + + CoreContext.UpdateCurrentThread(); + + if (CoreContext.CurrentThread != null) + { + CoreContext.CurrentThread.ClearExclusive(); + + CoreManager.GetThread(CoreContext.CurrentThread.Context.Work).Set(); + + CoreContext.CurrentThread.Context.Execute(); + + break; + } + } + + //If nothing was running before, then we are on a "external" + //HLE thread, we don't need to wait. + if (!HasThreadExecuting) + { + return; + } + } + } + + CoreManager.GetThread(Thread.CurrentThread).WaitOne(); + } + + private void PreemptCurrentThread() + { + //Preempts current thread every 10 milliseconds on a round-robin fashion, + //when multi core scheduling is disabled, to try ensuring that all threads + //gets a chance to run. + while (KeepPreempting) + { + lock (CoreContexts) + { + KThread CurrentThread = CoreContexts[CurrentCore].CurrentThread; + + CurrentThread?.Context.RequestInterrupt(); + } + + PreemptThreads(); + + Thread.Sleep(RoundRobinTimeQuantumMs); + } + } + + public void StopThread(KThread Thread) + { + Thread.Context.StopExecution(); + + CoreManager.GetThread(Thread.Context.Work).Set(); + } + + public void RemoveThread(KThread Thread) + { + CoreManager.RemoveThread(Thread.Context.Work); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/IKFutureSchedulerObject.cs b/Ryujinx.HLE/HOS/Kernel/IKFutureSchedulerObject.cs new file mode 100644 index 0000000000..6a255e6552 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/IKFutureSchedulerObject.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + interface IKFutureSchedulerObject + { + void TimeUp(); + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs new file mode 100644 index 0000000000..4a0f955fe3 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs @@ -0,0 +1,678 @@ +using ChocolArm64.Memory; +using System.Collections.Generic; +using System.Linq; + +using static Ryujinx.HLE.HOS.ErrorCode; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KAddressArbiter + { + private const int HasListenersMask = 0x40000000; + + private Horizon System; + + public List CondVarThreads; + public List ArbiterThreads; + + public KAddressArbiter(Horizon System) + { + this.System = System; + + CondVarThreads = new List(); + ArbiterThreads = new List(); + } + + public long ArbitrateLock( + Process Process, + MemoryManager Memory, + int OwnerHandle, + long MutexAddress, + int RequesterHandle) + { + System.CriticalSectionLock.Lock(); + + KThread CurrentThread = System.Scheduler.GetCurrentThread(); + + CurrentThread.SignaledObj = null; + CurrentThread.ObjSyncResult = 0; + + if (!UserToKernelInt32(Memory, MutexAddress, out int MutexValue)) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);; + } + + if (MutexValue != (OwnerHandle | HasListenersMask)) + { + System.CriticalSectionLock.Unlock(); + + return 0; + } + + KThread MutexOwner = Process.HandleTable.GetObject(OwnerHandle); + + if (MutexOwner == null) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + + CurrentThread.MutexAddress = MutexAddress; + CurrentThread.ThreadHandleForUserMutex = RequesterHandle; + + MutexOwner.AddMutexWaiter(CurrentThread); + + CurrentThread.Reschedule(ThreadSchedState.Paused); + + System.CriticalSectionLock.Unlock(); + System.CriticalSectionLock.Lock(); + + if (CurrentThread.MutexOwner != null) + { + CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread); + } + + System.CriticalSectionLock.Unlock(); + + return (uint)CurrentThread.ObjSyncResult; + } + + public long ArbitrateUnlock(MemoryManager Memory, long MutexAddress) + { + System.CriticalSectionLock.Lock(); + + KThread CurrentThread = System.Scheduler.GetCurrentThread(); + + (long Result, KThread NewOwnerThread) = MutexUnlock(Memory, CurrentThread, MutexAddress); + + if (Result != 0 && NewOwnerThread != null) + { + NewOwnerThread.SignaledObj = null; + NewOwnerThread.ObjSyncResult = (int)Result; + } + + System.CriticalSectionLock.Unlock(); + + return Result; + } + + public long WaitProcessWideKeyAtomic( + MemoryManager Memory, + long MutexAddress, + long CondVarAddress, + int ThreadHandle, + long Timeout) + { + System.CriticalSectionLock.Lock(); + + KThread CurrentThread = System.Scheduler.GetCurrentThread(); + + CurrentThread.SignaledObj = null; + CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout); + + if (CurrentThread.ShallBeTerminated || + CurrentThread.SchedFlags == ThreadSchedState.TerminationPending) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); + } + + (long Result, _) = MutexUnlock(Memory, CurrentThread, MutexAddress); + + if (Result != 0) + { + System.CriticalSectionLock.Unlock(); + + return Result; + } + + CurrentThread.MutexAddress = MutexAddress; + CurrentThread.ThreadHandleForUserMutex = ThreadHandle; + CurrentThread.CondVarAddress = CondVarAddress; + + CondVarThreads.Add(CurrentThread); + + if (Timeout != 0) + { + CurrentThread.Reschedule(ThreadSchedState.Paused); + + if (Timeout > 0) + { + System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout); + } + } + + System.CriticalSectionLock.Unlock(); + + if (Timeout > 0) + { + System.TimeManager.UnscheduleFutureInvocation(CurrentThread); + } + + System.CriticalSectionLock.Lock(); + + if (CurrentThread.MutexOwner != null) + { + CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread); + } + + CondVarThreads.Remove(CurrentThread); + + System.CriticalSectionLock.Unlock(); + + return (uint)CurrentThread.ObjSyncResult; + } + + private (long, KThread) MutexUnlock(MemoryManager Memory, KThread CurrentThread, long MutexAddress) + { + KThread NewOwnerThread = CurrentThread.RelinquishMutex(MutexAddress, out int Count); + + int MutexValue = 0; + + if (NewOwnerThread != null) + { + MutexValue = NewOwnerThread.ThreadHandleForUserMutex; + + if (Count >= 2) + { + MutexValue |= HasListenersMask; + } + + NewOwnerThread.SignaledObj = null; + NewOwnerThread.ObjSyncResult = 0; + + NewOwnerThread.ReleaseAndResume(); + } + + long Result = 0; + + if (!KernelToUserInt32(Memory, MutexAddress, MutexValue)) + { + Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + return (Result, NewOwnerThread); + } + + public void SignalProcessWideKey(Process Process, MemoryManager Memory, long Address, int Count) + { + Queue SignaledThreads = new Queue(); + + System.CriticalSectionLock.Lock(); + + IOrderedEnumerable SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority); + + foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address)) + { + TryAcquireMutex(Process, Memory, Thread); + + SignaledThreads.Enqueue(Thread); + + //If the count is <= 0, we should signal all threads waiting. + if (Count >= 1 && --Count == 0) + { + break; + } + } + + while (SignaledThreads.TryDequeue(out KThread Thread)) + { + CondVarThreads.Remove(Thread); + } + + System.CriticalSectionLock.Unlock(); + } + + private KThread TryAcquireMutex(Process Process, MemoryManager Memory, KThread Requester) + { + long Address = Requester.MutexAddress; + + Memory.SetExclusive(0, Address); + + if (!UserToKernelInt32(Memory, Address, out int MutexValue)) + { + //Invalid address. + Memory.ClearExclusive(0); + + Requester.SignaledObj = null; + Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + + return null; + } + + while (true) + { + if (Memory.TestExclusive(0, Address)) + { + if (MutexValue != 0) + { + //Update value to indicate there is a mutex waiter now. + Memory.WriteInt32(Address, MutexValue | HasListenersMask); + } + else + { + //No thread owning the mutex, assign to requesting thread. + Memory.WriteInt32(Address, Requester.ThreadHandleForUserMutex); + } + + Memory.ClearExclusiveForStore(0); + + break; + } + + Memory.SetExclusive(0, Address); + + MutexValue = Memory.ReadInt32(Address); + } + + if (MutexValue == 0) + { + //We now own the mutex. + Requester.SignaledObj = null; + Requester.ObjSyncResult = 0; + + Requester.ReleaseAndResume(); + + return null; + } + + MutexValue &= ~HasListenersMask; + + KThread MutexOwner = Process.HandleTable.GetObject(MutexValue); + + if (MutexOwner != null) + { + //Mutex already belongs to another thread, wait for it. + MutexOwner.AddMutexWaiter(Requester); + } + else + { + //Invalid mutex owner. + Requester.SignaledObj = null; + Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + Requester.ReleaseAndResume(); + } + + return MutexOwner; + } + + public long WaitForAddressIfEqual(MemoryManager Memory, long Address, int Value, long Timeout) + { + KThread CurrentThread = System.Scheduler.GetCurrentThread(); + + System.CriticalSectionLock.Lock(); + + if (CurrentThread.ShallBeTerminated || + CurrentThread.SchedFlags == ThreadSchedState.TerminationPending) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); + } + + CurrentThread.SignaledObj = null; + CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout); + + if (!UserToKernelInt32(Memory, Address, out int CurrentValue)) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + if (CurrentValue == Value) + { + if (Timeout == 0) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + + CurrentThread.MutexAddress = Address; + CurrentThread.WaitingInArbitration = true; + + InsertSortedByPriority(ArbiterThreads, CurrentThread); + + CurrentThread.Reschedule(ThreadSchedState.Paused); + + if (Timeout > 0) + { + System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout); + } + + System.CriticalSectionLock.Unlock(); + + if (Timeout > 0) + { + System.TimeManager.UnscheduleFutureInvocation(CurrentThread); + } + + System.CriticalSectionLock.Lock(); + + if (CurrentThread.WaitingInArbitration) + { + ArbiterThreads.Remove(CurrentThread); + + CurrentThread.WaitingInArbitration = false; + } + + System.CriticalSectionLock.Unlock(); + + return CurrentThread.ObjSyncResult; + } + + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + + public long WaitForAddressIfLessThan( + MemoryManager Memory, + long Address, + int Value, + bool ShouldDecrement, + long Timeout) + { + KThread CurrentThread = System.Scheduler.GetCurrentThread(); + + System.CriticalSectionLock.Lock(); + + if (CurrentThread.ShallBeTerminated || + CurrentThread.SchedFlags == ThreadSchedState.TerminationPending) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); + } + + CurrentThread.SignaledObj = null; + CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout); + + //If ShouldDecrement is true, do atomic decrement of the value at Address. + Memory.SetExclusive(0, Address); + + if (!UserToKernelInt32(Memory, Address, out int CurrentValue)) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + if (ShouldDecrement) + { + while (CurrentValue < Value) + { + if (Memory.TestExclusive(0, Address)) + { + Memory.WriteInt32(Address, CurrentValue - 1); + + Memory.ClearExclusiveForStore(0); + + break; + } + + Memory.SetExclusive(0, Address); + + CurrentValue = Memory.ReadInt32(Address); + } + } + + Memory.ClearExclusive(0); + + if (CurrentValue < Value) + { + if (Timeout == 0) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + + CurrentThread.MutexAddress = Address; + CurrentThread.WaitingInArbitration = true; + + InsertSortedByPriority(ArbiterThreads, CurrentThread); + + CurrentThread.Reschedule(ThreadSchedState.Paused); + + if (Timeout > 0) + { + System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout); + } + + System.CriticalSectionLock.Unlock(); + + if (Timeout > 0) + { + System.TimeManager.UnscheduleFutureInvocation(CurrentThread); + } + + System.CriticalSectionLock.Lock(); + + if (CurrentThread.WaitingInArbitration) + { + ArbiterThreads.Remove(CurrentThread); + + CurrentThread.WaitingInArbitration = false; + } + + System.CriticalSectionLock.Unlock(); + + return CurrentThread.ObjSyncResult; + } + + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + + private void InsertSortedByPriority(List Threads, KThread Thread) + { + int NextIndex = -1; + + for (int Index = 0; Index < Threads.Count; Index++) + { + if (Threads[Index].DynamicPriority > Thread.DynamicPriority) + { + NextIndex = Index; + + break; + } + } + + if (NextIndex != -1) + { + Threads.Insert(NextIndex, Thread); + } + else + { + Threads.Add(Thread); + } + } + + public long Signal(long Address, int Count) + { + System.CriticalSectionLock.Lock(); + + WakeArbiterThreads(Address, Count); + + System.CriticalSectionLock.Unlock(); + + return 0; + } + + public long SignalAndIncrementIfEqual(MemoryManager Memory, long Address, int Value, int Count) + { + System.CriticalSectionLock.Lock(); + + Memory.SetExclusive(0, Address); + + if (!UserToKernelInt32(Memory, Address, out int CurrentValue)) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + while (CurrentValue == Value) + { + if (Memory.TestExclusive(0, Address)) + { + Memory.WriteInt32(Address, CurrentValue + 1); + + Memory.ClearExclusiveForStore(0); + + break; + } + + Memory.SetExclusive(0, Address); + + CurrentValue = Memory.ReadInt32(Address); + } + + Memory.ClearExclusive(0); + + if (CurrentValue != Value) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + + WakeArbiterThreads(Address, Count); + + System.CriticalSectionLock.Unlock(); + + return 0; + } + + public long SignalAndModifyIfEqual(MemoryManager Memory, long Address, int Value, int Count) + { + System.CriticalSectionLock.Lock(); + + int Offset; + + //The value is decremented if the number of threads waiting is less + //or equal to the Count of threads to be signaled, or Count is zero + //or negative. It is incremented if there are no threads waiting. + int WaitingCount = 0; + + foreach (KThread Thread in ArbiterThreads.Where(x => x.MutexAddress == Address)) + { + if (++WaitingCount > Count) + { + break; + } + } + + if (WaitingCount > 0) + { + Offset = WaitingCount <= Count || Count <= 0 ? -1 : 0; + } + else + { + Offset = 1; + } + + Memory.SetExclusive(0, Address); + + if (!UserToKernelInt32(Memory, Address, out int CurrentValue)) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + while (CurrentValue == Value) + { + if (Memory.TestExclusive(0, Address)) + { + Memory.WriteInt32(Address, CurrentValue + Offset); + + Memory.ClearExclusiveForStore(0); + + break; + } + + Memory.SetExclusive(0, Address); + + CurrentValue = Memory.ReadInt32(Address); + } + + Memory.ClearExclusive(0); + + if (CurrentValue != Value) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + + WakeArbiterThreads(Address, Count); + + System.CriticalSectionLock.Unlock(); + + return 0; + } + + private void WakeArbiterThreads(long Address, int Count) + { + Queue SignaledThreads = new Queue(); + + foreach (KThread Thread in ArbiterThreads.Where(x => x.MutexAddress == Address)) + { + SignaledThreads.Enqueue(Thread); + + //If the count is <= 0, we should signal all threads waiting. + if (Count >= 1 && --Count == 0) + { + break; + } + } + + while (SignaledThreads.TryDequeue(out KThread Thread)) + { + Thread.SignaledObj = null; + Thread.ObjSyncResult = 0; + + Thread.ReleaseAndResume(); + + Thread.WaitingInArbitration = false; + + ArbiterThreads.Remove(Thread); + } + } + + private bool UserToKernelInt32(MemoryManager Memory, long Address, out int Value) + { + if (Memory.IsMapped(Address)) + { + Value = Memory.ReadInt32(Address); + + return true; + } + + Value = 0; + + return false; + } + + private bool KernelToUserInt32(MemoryManager Memory, long Address, int Value) + { + if (Memory.IsMapped(Address)) + { + Memory.WriteInt32ToSharedAddr(Address, Value); + + return true; + } + + return false; + } + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs new file mode 100644 index 0000000000..02354e16ad --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs @@ -0,0 +1,66 @@ +using Ryujinx.Common; +using System; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KCoreContext + { + private KScheduler Scheduler; + + private HleCoreManager CoreManager; + + public bool ContextSwitchNeeded { get; private set; } + + public KThread CurrentThread { get; private set; } + public KThread SelectedThread { get; private set; } + + public KCoreContext(KScheduler Scheduler, HleCoreManager CoreManager) + { + this.Scheduler = Scheduler; + this.CoreManager = CoreManager; + } + + public void SelectThread(KThread Thread) + { + SelectedThread = Thread; + + if (Thread != null) + { + Thread.LastScheduledTicks = PerformanceCounter.ElapsedMilliseconds; + } + + if (SelectedThread != CurrentThread) + { + ContextSwitchNeeded = true; + } + } + + public void UpdateCurrentThread() + { + ContextSwitchNeeded = false; + + CurrentThread = SelectedThread; + } + + public void ContextSwitch() + { + ContextSwitchNeeded = false; + + if (CurrentThread != null) + { + CoreManager.GetThread(CurrentThread.Context.Work).Reset(); + } + + CurrentThread = SelectedThread; + + if (CurrentThread != null) + { + CurrentThread.ClearExclusive(); + + CoreManager.GetThread(CurrentThread.Context.Work).Set(); + + CurrentThread.Context.Execute(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KEvent.cs b/Ryujinx.HLE/HOS/Kernel/KEvent.cs index eaaafaba33..106d1b4092 100644 --- a/Ryujinx.HLE/HOS/Kernel/KEvent.cs +++ b/Ryujinx.HLE/HOS/Kernel/KEvent.cs @@ -1,4 +1,14 @@ namespace Ryujinx.HLE.HOS.Kernel { - class KEvent : KSynchronizationObject { } + class KEvent + { + public KReadableEvent ReadableEvent { get; private set; } + public KWritableEvent WritableEvent { get; private set; } + + public KEvent(Horizon System) + { + ReadableEvent = new KReadableEvent(System, this); + WritableEvent = new KWritableEvent(this); + } + } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs b/Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs new file mode 100644 index 0000000000..9863a374bd --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KHandleEntry + { + public KHandleEntry Next { get; set; } + + public int Index { get; private set; } + + public ushort HandleId { get; set; } + public object Obj { get; set; } + + public KHandleEntry(int Index) + { + this.Index = Index; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs index 3afdb57022..4cfb2aa20a 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel private LinkedList Blocks; - private AMemory CpuMemory; + private MemoryManager CpuMemory; private ArenaAllocator Allocator; @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Kernel break; case AddressSpaceType.Addr39Bits: - CodeRegionStart = 0; + CodeRegionStart = 0x8000000; CodeRegionSize = 0x80000000; MapRegionSize = 0x1000000000; HeapRegionSize = 0x180000000; @@ -148,6 +148,92 @@ namespace Ryujinx.HLE.HOS.Kernel } } + public long MapProcessCodeMemory(long Dst, long Src, long Size) + { + lock (Blocks) + { + long PagesCount = Size / PageSize; + + bool Success = IsUnmapped(Dst, Size); + + Success &= CheckRange( + Src, + Size, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + long PA = CpuMemory.GetPhysicalAddress(Src); + + InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute); + InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None); + + CpuMemory.Map(Dst, PA, Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long UnmapProcessCodeMemory(long Dst, long Src, long Size) + { + lock (Blocks) + { + long PagesCount = Size / PageSize; + + bool Success = CheckRange( + Dst, + Size, + MemoryState.Mask, + MemoryState.CodeStatic, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + Success &= CheckRange( + Src, + Size, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + InsertBlock(Dst, PagesCount, MemoryState.Unmapped); + InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); + + CpuMemory.Unmap(Dst, Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission) { long PagesCount = Size / PageSize; @@ -755,6 +841,18 @@ namespace Ryujinx.HLE.HOS.Kernel } } + public bool HleIsUnmapped(long Position, long Size) + { + bool Result = false; + + lock (Blocks) + { + Result = IsUnmapped(Position, Size); + } + + return Result; + } + private bool IsUnmapped(long Position, long Size) { return CheckRange( diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs index db0eaa44f9..682f08d4ff 100644 --- a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs @@ -1,34 +1,183 @@ -using System.Collections.Generic; +using System; namespace Ryujinx.HLE.HOS.Kernel { class KProcessHandleTable { - private IdDictionary Handles; + private const int SelfThreadHandle = (0x1ffff << 15) | 0; + private const int SelfProcessHandle = (0x1ffff << 15) | 1; - public KProcessHandleTable() + private Horizon System; + + private KHandleEntry[] Table; + + private KHandleEntry TableHead; + private KHandleEntry NextFreeEntry; + + private int ActiveSlotsCount; + + private int Size; + + private ushort IdCounter; + + private object LockObj; + + public KProcessHandleTable(Horizon System, int Size = 1024) { - Handles = new IdDictionary(); + this.System = System; + this.Size = Size; + + IdCounter = 1; + + Table = new KHandleEntry[Size]; + + TableHead = new KHandleEntry(0); + + KHandleEntry Entry = TableHead; + + for (int Index = 0; Index < Size; Index++) + { + Table[Index] = Entry; + + Entry.Next = new KHandleEntry(Index + 1); + + Entry = Entry.Next; + } + + Table[Size - 1].Next = null; + + NextFreeEntry = TableHead; + + LockObj = new object(); } - public int OpenHandle(object Obj) + public KernelResult GenerateHandle(object Obj, out int Handle) { - return Handles.Add(Obj); + Handle = 0; + + lock (LockObj) + { + if (ActiveSlotsCount >= Size) + { + return KernelResult.HandleTableFull; + } + + KHandleEntry Entry = NextFreeEntry; + + NextFreeEntry = Entry.Next; + + Entry.Obj = Obj; + Entry.HandleId = IdCounter; + + ActiveSlotsCount++; + + Handle = (int)((IdCounter << 15) & (uint)0xffff8000) | Entry.Index; + + if ((short)(IdCounter + 1) >= 0) + { + IdCounter++; + } + else + { + IdCounter = 1; + } + } + + return KernelResult.Success; } - public T GetData(int Handle) + public bool CloseHandle(int Handle) { - return Handles.GetData(Handle); + if ((Handle >> 30) != 0 || + Handle == SelfThreadHandle || + Handle == SelfProcessHandle) + { + return false; + } + + int Index = (Handle >> 0) & 0x7fff; + int HandleId = (Handle >> 15); + + bool Result = false; + + lock (LockObj) + { + if (HandleId != 0 && Index < Size) + { + KHandleEntry Entry = Table[Index]; + + if (Entry.Obj != null && Entry.HandleId == HandleId) + { + Entry.Obj = null; + Entry.Next = NextFreeEntry; + + NextFreeEntry = Entry; + + ActiveSlotsCount--; + + Result = true; + } + } + } + + return Result; } - public object CloseHandle(int Handle) + public T GetObject(int Handle) { - return Handles.Delete(Handle); + int Index = (Handle >> 0) & 0x7fff; + int HandleId = (Handle >> 15); + + lock (LockObj) + { + if ((Handle >> 30) == 0 && HandleId != 0) + { + KHandleEntry Entry = Table[Index]; + + if (Entry.HandleId == HandleId && Entry.Obj is T Obj) + { + return Obj; + } + } + } + + return default(T); } - public ICollection Clear() + public KThread GetKThread(int Handle) { - return Handles.Clear(); + if (Handle == SelfThreadHandle) + { + return System.Scheduler.GetCurrentThread(); + } + else + { + return GetObject(Handle); + } + } + + public void Destroy() + { + lock (LockObj) + { + for (int Index = 0; Index < Size; Index++) + { + KHandleEntry Entry = Table[Index]; + + if (Entry.Obj != null) + { + if (Entry.Obj is IDisposable DisposableObj) + { + DisposableObj.Dispose(); + } + + Entry.Obj = null; + Entry.Next = NextFreeEntry; + + NextFreeEntry = Entry; + } + } + } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs b/Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs deleted file mode 100644 index 2120f16c8e..0000000000 --- a/Ryujinx.HLE/HOS/Kernel/KProcessScheduler.cs +++ /dev/null @@ -1,370 +0,0 @@ -using Ryujinx.HLE.Logging; -using System; -using System.Collections.Concurrent; -using System.Threading; - -namespace Ryujinx.HLE.HOS.Kernel -{ - class KProcessScheduler : IDisposable - { - private ConcurrentDictionary AllThreads; - - private ThreadQueue WaitingToRun; - - private KThread[] CoreThreads; - - private bool[] CoreReschedule; - - private object SchedLock; - - private Logger Log; - - public KProcessScheduler(Logger Log) - { - this.Log = Log; - - AllThreads = new ConcurrentDictionary(); - - WaitingToRun = new ThreadQueue(); - - CoreThreads = new KThread[4]; - - CoreReschedule = new bool[4]; - - SchedLock = new object(); - } - - public void StartThread(KThread Thread) - { - lock (SchedLock) - { - SchedulerThread SchedThread = new SchedulerThread(Thread); - - if (!AllThreads.TryAdd(Thread, SchedThread)) - { - return; - } - - if (TryAddToCore(Thread)) - { - Thread.Thread.Execute(); - - PrintDbgThreadInfo(Thread, "running."); - } - else - { - WaitingToRun.Push(SchedThread); - - PrintDbgThreadInfo(Thread, "waiting to run."); - } - } - } - - public void RemoveThread(KThread Thread) - { - PrintDbgThreadInfo(Thread, "exited."); - - lock (SchedLock) - { - if (AllThreads.TryRemove(Thread, out SchedulerThread SchedThread)) - { - WaitingToRun.Remove(SchedThread); - - SchedThread.Dispose(); - } - - int ActualCore = Thread.ActualCore; - - SchedulerThread NewThread = WaitingToRun.Pop(ActualCore); - - if (NewThread == null) - { - Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {ActualCore}!"); - - CoreThreads[ActualCore] = null; - - return; - } - - NewThread.Thread.ActualCore = ActualCore; - - RunThread(NewThread); - } - } - - public void SetThreadActivity(KThread Thread, bool Active) - { - SchedulerThread SchedThread = AllThreads[Thread]; - - SchedThread.IsActive = Active; - - if (Active) - { - SchedThread.WaitActivity.Set(); - } - else - { - SchedThread.WaitActivity.Reset(); - } - } - - public void EnterWait(KThread Thread, int TimeoutMs = Timeout.Infinite) - { - SchedulerThread SchedThread = AllThreads[Thread]; - - Suspend(Thread); - - SchedThread.WaitSync.WaitOne(TimeoutMs); - - TryResumingExecution(SchedThread); - } - - public void WakeUp(KThread Thread) - { - AllThreads[Thread].WaitSync.Set(); - } - - public void ForceWakeUp(KThread Thread) - { - if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) - { - SchedThread.WaitSync.Set(); - SchedThread.WaitActivity.Set(); - SchedThread.WaitSched.Set(); - } - } - - public void ChangeCore(KThread Thread, int IdealCore, int CoreMask) - { - lock (SchedLock) - { - if (IdealCore != -3) - { - Thread.IdealCore = IdealCore; - } - - Thread.CoreMask = CoreMask; - - if (AllThreads.ContainsKey(Thread)) - { - SetReschedule(Thread.ActualCore); - - SchedulerThread SchedThread = AllThreads[Thread]; - - //Note: Aways if the thread is on the queue first, and try - //adding to a new core later, to ensure that a thread that - //is already running won't be added to another core. - if (WaitingToRun.HasThread(SchedThread) && TryAddToCore(Thread)) - { - WaitingToRun.Remove(SchedThread); - - RunThread(SchedThread); - } - } - } - } - - public void Suspend(KThread Thread) - { - lock (SchedLock) - { - PrintDbgThreadInfo(Thread, "suspended."); - - int ActualCore = Thread.ActualCore; - - CoreReschedule[ActualCore] = false; - - SchedulerThread SchedThread = WaitingToRun.Pop(ActualCore); - - if (SchedThread != null) - { - SchedThread.Thread.ActualCore = ActualCore; - - CoreThreads[ActualCore] = SchedThread.Thread; - - RunThread(SchedThread); - } - else - { - Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ActualCore}!"); - - CoreThreads[ActualCore] = null; - } - } - } - - public void SetReschedule(int Core) - { - lock (SchedLock) - { - CoreReschedule[Core] = true; - } - } - - public void Reschedule(KThread Thread) - { - bool NeedsReschedule; - - lock (SchedLock) - { - int ActualCore = Thread.ActualCore; - - NeedsReschedule = CoreReschedule[ActualCore]; - - CoreReschedule[ActualCore] = false; - } - - if (NeedsReschedule) - { - Yield(Thread, Thread.ActualPriority - 1); - } - } - - public void Yield(KThread Thread) - { - Yield(Thread, Thread.ActualPriority); - } - - private void Yield(KThread Thread, int MinPriority) - { - PrintDbgThreadInfo(Thread, "yielded execution."); - - lock (SchedLock) - { - int ActualCore = Thread.ActualCore; - - SchedulerThread NewThread = WaitingToRun.Pop(ActualCore, MinPriority); - - if (NewThread != null) - { - NewThread.Thread.ActualCore = ActualCore; - - CoreThreads[ActualCore] = NewThread.Thread; - - RunThread(NewThread); - } - else - { - CoreThreads[ActualCore] = null; - } - } - - Resume(Thread); - } - - public void Resume(KThread Thread) - { - TryResumingExecution(AllThreads[Thread]); - } - - private void TryResumingExecution(SchedulerThread SchedThread) - { - KThread Thread = SchedThread.Thread; - - PrintDbgThreadInfo(Thread, "trying to resume..."); - - SchedThread.WaitActivity.WaitOne(); - - lock (SchedLock) - { - if (TryAddToCore(Thread)) - { - PrintDbgThreadInfo(Thread, "resuming execution..."); - - return; - } - - WaitingToRun.Push(SchedThread); - - SetReschedule(Thread.ProcessorId); - - PrintDbgThreadInfo(Thread, "entering wait state..."); - } - - SchedThread.WaitSched.WaitOne(); - - PrintDbgThreadInfo(Thread, "resuming execution..."); - } - - private void RunThread(SchedulerThread SchedThread) - { - if (!SchedThread.Thread.Thread.Execute()) - { - PrintDbgThreadInfo(SchedThread.Thread, "waked."); - - SchedThread.WaitSched.Set(); - } - else - { - PrintDbgThreadInfo(SchedThread.Thread, "running."); - } - } - - public void Resort(KThread Thread) - { - if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) - { - WaitingToRun.Resort(SchedThread); - } - } - - private bool TryAddToCore(KThread Thread) - { - //First, try running it on Ideal Core. - int IdealCore = Thread.IdealCore; - - if (IdealCore != -1 && CoreThreads[IdealCore] == null) - { - Thread.ActualCore = IdealCore; - - CoreThreads[IdealCore] = Thread; - - return true; - } - - //If that fails, then try running on any core allowed by Core Mask. - int CoreMask = Thread.CoreMask; - - for (int Core = 0; Core < CoreThreads.Length; Core++, CoreMask >>= 1) - { - if ((CoreMask & 1) != 0 && CoreThreads[Core] == null) - { - Thread.ActualCore = Core; - - CoreThreads[Core] = Thread; - - return true; - } - } - - return false; - } - - private void PrintDbgThreadInfo(KThread Thread, string Message) - { - Log.PrintDebug(LogClass.KernelScheduler, "(" + - "ThreadId = " + Thread.ThreadId + ", " + - "CoreMask = 0x" + Thread.CoreMask.ToString("x1") + ", " + - "ActualCore = " + Thread.ActualCore + ", " + - "IdealCore = " + Thread.IdealCore + ", " + - "ActualPriority = " + Thread.ActualPriority + ", " + - "WantedPriority = " + Thread.WantedPriority + ") " + Message); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - foreach (SchedulerThread SchedThread in AllThreads.Values) - { - SchedThread.Dispose(); - } - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs new file mode 100644 index 0000000000..d43fe8249e --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs @@ -0,0 +1,62 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KReadableEvent : KSynchronizationObject + { + private KEvent Parent; + + private bool Signaled; + + public KReadableEvent(Horizon System, KEvent Parent) : base(System) + { + this.Parent = Parent; + } + + public override void Signal() + { + System.CriticalSectionLock.Lock(); + + if (!Signaled) + { + Signaled = true; + + base.Signal(); + } + + System.CriticalSectionLock.Unlock(); + } + + public KernelResult Clear() + { + Signaled = false; + + return KernelResult.Success; + } + + public KernelResult ClearIfSignaled() + { + KernelResult Result; + + System.CriticalSectionLock.Lock(); + + if (Signaled) + { + Signaled = false; + + Result = KernelResult.Success; + } + else + { + Result = KernelResult.InvalidState; + } + + System.CriticalSectionLock.Unlock(); + + return Result; + } + + public override bool IsSignaled() + { + return Signaled; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs b/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs new file mode 100644 index 0000000000..30c1a88051 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs @@ -0,0 +1,93 @@ +using ChocolArm64; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KRecursiveLock + { + private Horizon System; + + public object LockObj { get; private set; } + + private int RecursionCount; + + public KRecursiveLock(Horizon System) + { + this.System = System; + + LockObj = new object(); + } + + public void Lock() + { + Monitor.Enter(LockObj); + + RecursionCount++; + } + + public void Unlock() + { + if (RecursionCount == 0) + { + return; + } + + bool DoContextSwitch = false; + + if (--RecursionCount == 0) + { + if (System.Scheduler.ThreadReselectionRequested) + { + System.Scheduler.SelectThreads(); + } + + Monitor.Exit(LockObj); + + if (System.Scheduler.MultiCoreScheduling) + { + lock (System.Scheduler.CoreContexts) + { + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + KCoreContext CoreContext = System.Scheduler.CoreContexts[Core]; + + if (CoreContext.ContextSwitchNeeded) + { + CpuThread CurrentHleThread = CoreContext.CurrentThread?.Context; + + if (CurrentHleThread == null) + { + //Nothing is running, we can perform the context switch immediately. + CoreContext.ContextSwitch(); + } + else if (CurrentHleThread.IsCurrentThread()) + { + //Thread running on the current core, context switch will block. + DoContextSwitch = true; + } + else + { + //Thread running on another core, request a interrupt. + CurrentHleThread.RequestInterrupt(); + } + } + } + } + } + else + { + DoContextSwitch = true; + } + } + else + { + Monitor.Exit(LockObj); + } + + if (DoContextSwitch) + { + System.Scheduler.ContextSwitch(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs new file mode 100644 index 0000000000..3cfda4197f --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs @@ -0,0 +1,228 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.HLE.HOS.Kernel +{ + partial class KScheduler : IDisposable + { + public const int PrioritiesCount = 64; + public const int CpuCoresCount = 4; + + private const int PreemptionPriorityCores012 = 59; + private const int PreemptionPriorityCore3 = 63; + + private Horizon System; + + public KSchedulingData SchedulingData { get; private set; } + + public KCoreContext[] CoreContexts { get; private set; } + + public bool ThreadReselectionRequested { get; set; } + + public KScheduler(Horizon System) + { + this.System = System; + + SchedulingData = new KSchedulingData(); + + CoreManager = new HleCoreManager(); + + CoreContexts = new KCoreContext[CpuCoresCount]; + + for (int Core = 0; Core < CpuCoresCount; Core++) + { + CoreContexts[Core] = new KCoreContext(this, CoreManager); + } + } + + private void PreemptThreads() + { + System.CriticalSectionLock.Lock(); + + PreemptThread(PreemptionPriorityCores012, 0); + PreemptThread(PreemptionPriorityCores012, 1); + PreemptThread(PreemptionPriorityCores012, 2); + PreemptThread(PreemptionPriorityCore3, 3); + + System.CriticalSectionLock.Unlock(); + } + + private void PreemptThread(int Prio, int Core) + { + IEnumerable ScheduledThreads = SchedulingData.ScheduledThreads(Core); + + KThread SelectedThread = ScheduledThreads.FirstOrDefault(x => x.DynamicPriority == Prio); + + //Yield priority queue. + if (SelectedThread != null) + { + SchedulingData.Reschedule(Prio, Core, SelectedThread); + } + + IEnumerable SuitableCandidates() + { + foreach (KThread Thread in SchedulingData.SuggestedThreads(Core)) + { + int SrcCore = Thread.CurrentCore; + + if (SrcCore >= 0) + { + KThread HighestPrioSrcCore = SchedulingData.ScheduledThreads(SrcCore).FirstOrDefault(); + + if (HighestPrioSrcCore != null && HighestPrioSrcCore.DynamicPriority < 2) + { + break; + } + + if (HighestPrioSrcCore == Thread) + { + continue; + } + } + + //If the candidate was scheduled after the current thread, then it's not worth it. + if (SelectedThread == null || SelectedThread.LastScheduledTicks >= Thread.LastScheduledTicks) + { + yield return Thread; + } + } + } + + //Select candidate threads that could run on this core. + //Only take into account threads that are not yet selected. + KThread Dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == Prio); + + if (Dst != null) + { + SchedulingData.TransferToCore(Prio, Core, Dst); + + SelectedThread = Dst; + } + + //If the priority of the currently selected thread is lower than preemption priority, + //then allow threads with lower priorities to be selected aswell. + if (SelectedThread != null && SelectedThread.DynamicPriority > Prio) + { + Func Predicate = x => x.DynamicPriority >= SelectedThread.DynamicPriority; + + Dst = SuitableCandidates().FirstOrDefault(Predicate); + + if (Dst != null) + { + SchedulingData.TransferToCore(Dst.DynamicPriority, Core, Dst); + } + } + + ThreadReselectionRequested = true; + } + + public void SelectThreads() + { + ThreadReselectionRequested = false; + + for (int Core = 0; Core < CpuCoresCount; Core++) + { + KThread Thread = SchedulingData.ScheduledThreads(Core).FirstOrDefault(); + + CoreContexts[Core].SelectThread(Thread); + } + + for (int Core = 0; Core < CpuCoresCount; Core++) + { + //If the core is not idle (there's already a thread running on it), + //then we don't need to attempt load balancing. + if (SchedulingData.ScheduledThreads(Core).Any()) + { + continue; + } + + int[] SrcCoresHighestPrioThreads = new int[CpuCoresCount]; + + int SrcCoresHighestPrioThreadsCount = 0; + + KThread Dst = null; + + //Select candidate threads that could run on this core. + //Give preference to threads that are not yet selected. + foreach (KThread Thread in SchedulingData.SuggestedThreads(Core)) + { + if (Thread.CurrentCore < 0 || Thread != CoreContexts[Thread.CurrentCore].SelectedThread) + { + Dst = Thread; + + break; + } + + SrcCoresHighestPrioThreads[SrcCoresHighestPrioThreadsCount++] = Thread.CurrentCore; + } + + //Not yet selected candidate found. + if (Dst != null) + { + //Priorities < 2 are used for the kernel message dispatching + //threads, we should skip load balancing entirely. + if (Dst.DynamicPriority >= 2) + { + SchedulingData.TransferToCore(Dst.DynamicPriority, Core, Dst); + + CoreContexts[Core].SelectThread(Dst); + } + + continue; + } + + //All candiates are already selected, choose the best one + //(the first one that doesn't make the source core idle if moved). + for (int Index = 0; Index < SrcCoresHighestPrioThreadsCount; Index++) + { + int SrcCore = SrcCoresHighestPrioThreads[Index]; + + KThread Src = SchedulingData.ScheduledThreads(SrcCore).ElementAtOrDefault(1); + + if (Src != null) + { + //Run the second thread on the queue on the source core, + //move the first one to the current core. + KThread OrigSelectedCoreSrc = CoreContexts[SrcCore].SelectedThread; + + CoreContexts[SrcCore].SelectThread(Src); + + SchedulingData.TransferToCore(OrigSelectedCoreSrc.DynamicPriority, Core, OrigSelectedCoreSrc); + + CoreContexts[Core].SelectThread(OrigSelectedCoreSrc); + } + } + } + } + + public KThread GetCurrentThread() + { + lock (CoreContexts) + { + for (int Core = 0; Core < CpuCoresCount; Core++) + { + if (CoreContexts[Core].CurrentThread?.Context.IsCurrentThread() ?? false) + { + return CoreContexts[Core].CurrentThread; + } + } + } + + throw new InvalidOperationException("Current thread is not scheduled!"); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + KeepPreempting = false; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KSchedulingData.cs b/Ryujinx.HLE/HOS/Kernel/KSchedulingData.cs new file mode 100644 index 0000000000..ba2730a298 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KSchedulingData.cs @@ -0,0 +1,207 @@ +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KSchedulingData + { + private LinkedList[][] ScheduledThreadsPerPrioPerCore; + private LinkedList[][] SuggestedThreadsPerPrioPerCore; + + private long[] ScheduledPrioritiesPerCore; + private long[] SuggestedPrioritiesPerCore; + + public KSchedulingData() + { + SuggestedThreadsPerPrioPerCore = new LinkedList[KScheduler.PrioritiesCount][]; + ScheduledThreadsPerPrioPerCore = new LinkedList[KScheduler.PrioritiesCount][]; + + for (int Prio = 0; Prio < KScheduler.PrioritiesCount; Prio++) + { + SuggestedThreadsPerPrioPerCore[Prio] = new LinkedList[KScheduler.CpuCoresCount]; + ScheduledThreadsPerPrioPerCore[Prio] = new LinkedList[KScheduler.CpuCoresCount]; + + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + SuggestedThreadsPerPrioPerCore[Prio][Core] = new LinkedList(); + ScheduledThreadsPerPrioPerCore[Prio][Core] = new LinkedList(); + } + } + + ScheduledPrioritiesPerCore = new long[KScheduler.CpuCoresCount]; + SuggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount]; + } + + public IEnumerable SuggestedThreads(int Core) + { + return Iterate(SuggestedThreadsPerPrioPerCore, SuggestedPrioritiesPerCore, Core); + } + + public IEnumerable ScheduledThreads(int Core) + { + return Iterate(ScheduledThreadsPerPrioPerCore, ScheduledPrioritiesPerCore, Core); + } + + private IEnumerable Iterate(LinkedList[][] ListPerPrioPerCore, long[] Prios, int Core) + { + long PrioMask = Prios[Core]; + + int Prio = CountTrailingZeros(PrioMask); + + PrioMask &= ~(1L << Prio); + + while (Prio < KScheduler.PrioritiesCount) + { + LinkedList List = ListPerPrioPerCore[Prio][Core]; + + LinkedListNode Node = List.First; + + while (Node != null) + { + yield return Node.Value; + + Node = Node.Next; + } + + Prio = CountTrailingZeros(PrioMask); + + PrioMask &= ~(1L << Prio); + } + } + + private int CountTrailingZeros(long Value) + { + int Count = 0; + + while (((Value >> Count) & 0xf) == 0 && Count < 64) + { + Count += 4; + } + + while (((Value >> Count) & 1) == 0 && Count < 64) + { + Count++; + } + + return Count; + } + + public void TransferToCore(int Prio, int DstCore, KThread Thread) + { + bool Schedulable = Thread.DynamicPriority < KScheduler.PrioritiesCount; + + int SrcCore = Thread.CurrentCore; + + Thread.CurrentCore = DstCore; + + if (SrcCore == DstCore || !Schedulable) + { + return; + } + + if (SrcCore >= 0) + { + Unschedule(Prio, SrcCore, Thread); + } + + if (DstCore >= 0) + { + Unsuggest(Prio, DstCore, Thread); + Schedule(Prio, DstCore, Thread); + } + + if (SrcCore >= 0) + { + Suggest(Prio, SrcCore, Thread); + } + } + + public void Suggest(int Prio, int Core, KThread Thread) + { + if (Prio >= KScheduler.PrioritiesCount) + { + return; + } + + Thread.SiblingsPerCore[Core] = SuggestedQueue(Prio, Core).AddFirst(Thread); + + SuggestedPrioritiesPerCore[Core] |= 1L << Prio; + } + + public void Unsuggest(int Prio, int Core, KThread Thread) + { + if (Prio >= KScheduler.PrioritiesCount) + { + return; + } + + LinkedList Queue = SuggestedQueue(Prio, Core); + + Queue.Remove(Thread.SiblingsPerCore[Core]); + + if (Queue.First == null) + { + SuggestedPrioritiesPerCore[Core] &= ~(1L << Prio); + } + } + + public void Schedule(int Prio, int Core, KThread Thread) + { + if (Prio >= KScheduler.PrioritiesCount) + { + return; + } + + Thread.SiblingsPerCore[Core] = ScheduledQueue(Prio, Core).AddLast(Thread); + + ScheduledPrioritiesPerCore[Core] |= 1L << Prio; + } + + public void SchedulePrepend(int Prio, int Core, KThread Thread) + { + if (Prio >= KScheduler.PrioritiesCount) + { + return; + } + + Thread.SiblingsPerCore[Core] = ScheduledQueue(Prio, Core).AddFirst(Thread); + + ScheduledPrioritiesPerCore[Core] |= 1L << Prio; + } + + public void Reschedule(int Prio, int Core, KThread Thread) + { + LinkedList Queue = ScheduledQueue(Prio, Core); + + Queue.Remove(Thread.SiblingsPerCore[Core]); + + Thread.SiblingsPerCore[Core] = Queue.AddLast(Thread); + } + + public void Unschedule(int Prio, int Core, KThread Thread) + { + if (Prio >= KScheduler.PrioritiesCount) + { + return; + } + + LinkedList Queue = ScheduledQueue(Prio, Core); + + Queue.Remove(Thread.SiblingsPerCore[Core]); + + if (Queue.First == null) + { + ScheduledPrioritiesPerCore[Core] &= ~(1L << Prio); + } + } + + private LinkedList SuggestedQueue(int Prio, int Core) + { + return SuggestedThreadsPerPrioPerCore[Prio][Core]; + } + + private LinkedList ScheduledQueue(int Prio, int Core) + { + return ScheduledThreadsPerPrioPerCore[Prio][Core]; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs new file mode 100644 index 0000000000..57a6296c2b --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; + +using static Ryujinx.HLE.HOS.ErrorCode; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KSynchronization + { + private Horizon System; + + public KSynchronization(Horizon System) + { + this.System = System; + } + + public long WaitFor(KSynchronizationObject[] SyncObjs, long Timeout, ref int HndIndex) + { + long Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout); + + System.CriticalSectionLock.Lock(); + + //Check if objects are already signaled before waiting. + for (int Index = 0; Index < SyncObjs.Length; Index++) + { + if (!SyncObjs[Index].IsSignaled()) + { + continue; + } + + HndIndex = Index; + + System.CriticalSectionLock.Unlock(); + + return 0; + } + + if (Timeout == 0) + { + System.CriticalSectionLock.Unlock(); + + return Result; + } + + KThread CurrentThread = System.Scheduler.GetCurrentThread(); + + if (CurrentThread.ShallBeTerminated || + CurrentThread.SchedFlags == ThreadSchedState.TerminationPending) + { + Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); + } + else if (CurrentThread.SyncCancelled) + { + CurrentThread.SyncCancelled = false; + + Result = MakeError(ErrorModule.Kernel, KernelErr.Cancelled); + } + else + { + LinkedListNode[] SyncNodes = new LinkedListNode[SyncObjs.Length]; + + for (int Index = 0; Index < SyncObjs.Length; Index++) + { + SyncNodes[Index] = SyncObjs[Index].AddWaitingThread(CurrentThread); + } + + CurrentThread.WaitingSync = true; + CurrentThread.SignaledObj = null; + CurrentThread.ObjSyncResult = (int)Result; + + CurrentThread.Reschedule(ThreadSchedState.Paused); + + if (Timeout > 0) + { + System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout); + } + + System.CriticalSectionLock.Unlock(); + + CurrentThread.WaitingSync = false; + + if (Timeout > 0) + { + System.TimeManager.UnscheduleFutureInvocation(CurrentThread); + } + + System.CriticalSectionLock.Lock(); + + Result = (uint)CurrentThread.ObjSyncResult; + + HndIndex = -1; + + for (int Index = 0; Index < SyncObjs.Length; Index++) + { + SyncObjs[Index].RemoveWaitingThread(SyncNodes[Index]); + + if (SyncObjs[Index] == CurrentThread.SignaledObj) + { + HndIndex = Index; + } + } + } + + System.CriticalSectionLock.Unlock(); + + return Result; + } + + public void SignalObject(KSynchronizationObject SyncObj) + { + System.CriticalSectionLock.Lock(); + + if (SyncObj.IsSignaled()) + { + LinkedListNode Node = SyncObj.WaitingThreads.First; + + while (Node != null) + { + KThread Thread = Node.Value; + + if ((Thread.SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused) + { + Thread.SignaledObj = SyncObj; + Thread.ObjSyncResult = 0; + + Thread.Reschedule(ThreadSchedState.Running); + } + + Node = Node.Next; + } + } + + System.CriticalSectionLock.Unlock(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs index b83b00047e..28eac33069 100644 --- a/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs +++ b/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs @@ -1,28 +1,38 @@ -using System; -using System.Threading; +using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Kernel { - class KSynchronizationObject : IDisposable + class KSynchronizationObject { - public ManualResetEvent WaitEvent { get; private set; } + public LinkedList WaitingThreads; - public KSynchronizationObject() + protected Horizon System; + + public KSynchronizationObject(Horizon System) { - WaitEvent = new ManualResetEvent(false); + this.System = System; + + WaitingThreads = new LinkedList(); } - public void Dispose() + public LinkedListNode AddWaitingThread(KThread Thread) { - Dispose(true); + return WaitingThreads.AddLast(Thread); } - protected virtual void Dispose(bool Disposing) + public void RemoveWaitingThread(LinkedListNode Node) { - if (Disposing) - { - WaitEvent.Dispose(); - } + WaitingThreads.Remove(Node); + } + + public virtual void Signal() + { + System.Synchronization.SignalObject(this); + } + + public virtual bool IsSignaled() + { + return false; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KThread.cs b/Ryujinx.HLE/HOS/Kernel/KThread.cs index 171fefc519..73ee2322d0 100644 --- a/Ryujinx.HLE/HOS/Kernel/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/KThread.cs @@ -1,98 +1,883 @@ using ChocolArm64; +using System; using System.Collections.Generic; +using System.Linq; + +using static Ryujinx.HLE.HOS.ErrorCode; namespace Ryujinx.HLE.HOS.Kernel { - class KThread : KSynchronizationObject + class KThread : KSynchronizationObject, IKFutureSchedulerObject { - public AThread Thread { get; private set; } + public CpuThread Context { get; private set; } - public int CoreMask { get; set; } - - public long MutexAddress { get; set; } - public long CondVarAddress { get; set; } - public long ArbiterWaitAddress { get; set; } - - public bool CondVarSignaled { get; set; } - public bool ArbiterSignaled { get; set; } - - private Process Process; - - public List MutexWaiters { get; private set; } - - public KThread MutexOwner { get; set; } - - public int ActualPriority { get; private set; } - public int WantedPriority { get; private set; } - - public int ActualCore { get; set; } - public int ProcessorId { get; set; } - public int IdealCore { get; set; } - - public int WaitHandle { get; set; } - - public long LastPc { get; set; } + public long AffinityMask { get; set; } public int ThreadId { get; private set; } + public KSynchronizationObject SignaledObj; + + public long CondVarAddress { get; set; } + public long MutexAddress { get; set; } + + public Process Owner { get; private set; } + + public long LastScheduledTicks { get; set; } + + public LinkedListNode[] SiblingsPerCore { get; private set; } + + private LinkedListNode WithholderNode; + + private LinkedList MutexWaiters; + private LinkedListNode MutexWaiterNode; + + public KThread MutexOwner { get; private set; } + + public int ThreadHandleForUserMutex { get; set; } + + private ThreadSchedState ForcePauseFlags; + + public int ObjSyncResult { get; set; } + + public int DynamicPriority { get; set; } + public int CurrentCore { get; set; } + public int BasePriority { get; set; } + public int PreferredCore { get; set; } + + private long AffinityMaskOverride; + private int PreferredCoreOverride; + private int AffinityOverrideCount; + + public ThreadSchedState SchedFlags { get; private set; } + + public bool ShallBeTerminated { get; private set; } + + public bool SyncCancelled { get; set; } + public bool WaitingSync { get; set; } + + private bool HasExited; + + public bool WaitingInArbitration { get; set; } + + private KScheduler Scheduler; + + private KSchedulingData SchedulingData; + + public long LastPc { get; set; } + public KThread( - AThread Thread, - Process Process, - int ProcessorId, - int Priority, - int ThreadId) + CpuThread Thread, + Process Process, + Horizon System, + int ProcessorId, + int Priority, + int ThreadId) : base(System) { - this.Thread = Thread; - this.Process = Process; - this.ProcessorId = ProcessorId; - this.IdealCore = ProcessorId; - this.ThreadId = ThreadId; + this.ThreadId = ThreadId; - MutexWaiters = new List(); + Context = Thread; + Owner = Process; + PreferredCore = ProcessorId; + Scheduler = System.Scheduler; + SchedulingData = System.Scheduler.SchedulingData; - CoreMask = 1 << ProcessorId; + SiblingsPerCore = new LinkedListNode[KScheduler.CpuCoresCount]; - ActualPriority = WantedPriority = Priority; + MutexWaiters = new LinkedList(); + + AffinityMask = 1 << ProcessorId; + + DynamicPriority = BasePriority = Priority; + + CurrentCore = PreferredCore; + } + + public long Start() + { + long Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); + + System.CriticalSectionLock.Lock(); + + if (!ShallBeTerminated) + { + KThread CurrentThread = System.Scheduler.GetCurrentThread(); + + while (SchedFlags != ThreadSchedState.TerminationPending && + CurrentThread.SchedFlags != ThreadSchedState.TerminationPending && + !CurrentThread.ShallBeTerminated) + { + if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.None) + { + Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + + break; + } + + if (CurrentThread.ForcePauseFlags == ThreadSchedState.None) + { + if (Owner != null && ForcePauseFlags != ThreadSchedState.None) + { + CombineForcePauseFlags(); + } + + SetNewSchedFlags(ThreadSchedState.Running); + + Result = 0; + + break; + } + else + { + CurrentThread.CombineForcePauseFlags(); + + System.CriticalSectionLock.Unlock(); + System.CriticalSectionLock.Lock(); + + if (CurrentThread.ShallBeTerminated) + { + break; + } + } + } + } + + System.CriticalSectionLock.Unlock(); + + return Result; + } + + public void Exit() + { + System.CriticalSectionLock.Lock(); + + ForcePauseFlags &= ~ThreadSchedState.ExceptionalMask; + + ExitImpl(); + + System.CriticalSectionLock.Unlock(); + } + + private void ExitImpl() + { + System.CriticalSectionLock.Lock(); + + SetNewSchedFlags(ThreadSchedState.TerminationPending); + + HasExited = true; + + Signal(); + + System.CriticalSectionLock.Unlock(); + } + + public long Sleep(long Timeout) + { + System.CriticalSectionLock.Lock(); + + if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); + } + + SetNewSchedFlags(ThreadSchedState.Paused); + + if (Timeout > 0) + { + System.TimeManager.ScheduleFutureInvocation(this, Timeout); + } + + System.CriticalSectionLock.Unlock(); + + if (Timeout > 0) + { + System.TimeManager.UnscheduleFutureInvocation(this); + } + + return 0; + } + + public void Yield() + { + System.CriticalSectionLock.Lock(); + + if (SchedFlags != ThreadSchedState.Running) + { + System.CriticalSectionLock.Unlock(); + + System.Scheduler.ContextSwitch(); + + return; + } + + if (DynamicPriority < KScheduler.PrioritiesCount) + { + //Move current thread to the end of the queue. + SchedulingData.Reschedule(DynamicPriority, CurrentCore, this); + } + + Scheduler.ThreadReselectionRequested = true; + + System.CriticalSectionLock.Unlock(); + + System.Scheduler.ContextSwitch(); + } + + public void YieldWithLoadBalancing() + { + System.CriticalSectionLock.Lock(); + + int Prio = DynamicPriority; + int Core = CurrentCore; + + if (SchedFlags != ThreadSchedState.Running) + { + System.CriticalSectionLock.Unlock(); + + System.Scheduler.ContextSwitch(); + + return; + } + + KThread NextThreadOnCurrentQueue = null; + + if (DynamicPriority < KScheduler.PrioritiesCount) + { + //Move current thread to the end of the queue. + SchedulingData.Reschedule(Prio, Core, this); + + Func Predicate = x => x.DynamicPriority == Prio; + + NextThreadOnCurrentQueue = SchedulingData.ScheduledThreads(Core).FirstOrDefault(Predicate); + } + + IEnumerable SuitableCandidates() + { + foreach (KThread Thread in SchedulingData.SuggestedThreads(Core)) + { + int SrcCore = Thread.CurrentCore; + + if (SrcCore >= 0) + { + KThread SelectedSrcCore = Scheduler.CoreContexts[SrcCore].SelectedThread; + + if (SelectedSrcCore == Thread || ((SelectedSrcCore?.DynamicPriority ?? 2) < 2)) + { + continue; + } + } + + //If the candidate was scheduled after the current thread, then it's not worth it, + //unless the priority is higher than the current one. + if (NextThreadOnCurrentQueue.LastScheduledTicks >= Thread.LastScheduledTicks || + NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority) + { + yield return Thread; + } + } + } + + KThread Dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= Prio); + + if (Dst != null) + { + SchedulingData.TransferToCore(Dst.DynamicPriority, Core, Dst); + + Scheduler.ThreadReselectionRequested = true; + } + + if (this != NextThreadOnCurrentQueue) + { + Scheduler.ThreadReselectionRequested = true; + } + + System.CriticalSectionLock.Unlock(); + + System.Scheduler.ContextSwitch(); + } + + public void YieldAndWaitForLoadBalancing() + { + System.CriticalSectionLock.Lock(); + + if (SchedFlags != ThreadSchedState.Running) + { + System.CriticalSectionLock.Unlock(); + + System.Scheduler.ContextSwitch(); + + return; + } + + int Core = CurrentCore; + + SchedulingData.TransferToCore(DynamicPriority, -1, this); + + KThread SelectedThread = null; + + if (!SchedulingData.ScheduledThreads(Core).Any()) + { + foreach (KThread Thread in SchedulingData.SuggestedThreads(Core)) + { + if (Thread.CurrentCore < 0) + { + continue; + } + + KThread FirstCandidate = SchedulingData.ScheduledThreads(Thread.CurrentCore).FirstOrDefault(); + + if (FirstCandidate == Thread) + { + continue; + } + + if (FirstCandidate == null || FirstCandidate.DynamicPriority >= 2) + { + SchedulingData.TransferToCore(Thread.DynamicPriority, Core, Thread); + + SelectedThread = Thread; + } + + break; + } + } + + if (SelectedThread != this) + { + Scheduler.ThreadReselectionRequested = true; + } + + System.CriticalSectionLock.Unlock(); + + System.Scheduler.ContextSwitch(); } public void SetPriority(int Priority) { - WantedPriority = Priority; + System.CriticalSectionLock.Lock(); - UpdatePriority(); + BasePriority = Priority; + + UpdatePriorityInheritance(); + + System.CriticalSectionLock.Unlock(); } - public void UpdatePriority() + public long SetActivity(bool Pause) { - bool PriorityChanged; + long Result = 0; - lock (Process.ThreadSyncLock) + System.CriticalSectionLock.Lock(); + + ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask; + + if (LowNibble != ThreadSchedState.Paused && LowNibble != ThreadSchedState.Running) { - int OldPriority = ActualPriority; + System.CriticalSectionLock.Unlock(); - int CurrPriority = WantedPriority; + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } - foreach (KThread Thread in MutexWaiters) + System.CriticalSectionLock.Lock(); + + if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending) + { + if (Pause) { - int WantedPriority = Thread.WantedPriority; - - if (CurrPriority > WantedPriority) + //Pause, the force pause flag should be clear (thread is NOT paused). + if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) == 0) { - CurrPriority = WantedPriority; + ForcePauseFlags |= ThreadSchedState.ForcePauseFlag; + + CombineForcePauseFlags(); + } + else + { + Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState); } } + else + { + //Unpause, the force pause flag should be set (thread is paused). + if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) != 0) + { + ThreadSchedState OldForcePauseFlags = ForcePauseFlags; - PriorityChanged = CurrPriority != OldPriority; + ForcePauseFlags &= ~ThreadSchedState.ForcePauseFlag; - ActualPriority = CurrPriority; + if ((OldForcePauseFlags & ~ThreadSchedState.ForcePauseFlag) == ThreadSchedState.None) + { + ThreadSchedState OldSchedFlags = SchedFlags; + + SchedFlags &= ThreadSchedState.LowNibbleMask; + + AdjustScheduling(OldSchedFlags); + } + } + else + { + Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + } } - if (PriorityChanged) + System.CriticalSectionLock.Unlock(); + System.CriticalSectionLock.Unlock(); + + return Result; + } + + public void CancelSynchronization() + { + System.CriticalSectionLock.Lock(); + + if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.Paused || !WaitingSync) { - Process.Scheduler.Resort(this); - - MutexOwner?.UpdatePriority(); + SyncCancelled = true; } + else if (WithholderNode != null) + { + System.Withholders.Remove(WithholderNode); + + SetNewSchedFlags(ThreadSchedState.Running); + + WithholderNode = null; + + SyncCancelled = true; + } + else + { + SignaledObj = null; + ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Cancelled); + + SetNewSchedFlags(ThreadSchedState.Running); + + SyncCancelled = false; + } + + System.CriticalSectionLock.Unlock(); + } + + public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask) + { + System.CriticalSectionLock.Lock(); + + bool UseOverride = AffinityOverrideCount != 0; + + //The value -3 is "do not change the preferred core". + if (NewCore == -3) + { + NewCore = UseOverride ? PreferredCoreOverride : PreferredCore; + + if ((NewAffinityMask & (1 << NewCore)) == 0) + { + System.CriticalSectionLock.Unlock(); + + return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); + } + } + + if (UseOverride) + { + PreferredCoreOverride = NewCore; + AffinityMaskOverride = NewAffinityMask; + } + else + { + long OldAffinityMask = AffinityMask; + + PreferredCore = NewCore; + AffinityMask = NewAffinityMask; + + if (OldAffinityMask != NewAffinityMask) + { + int OldCore = CurrentCore; + + if (CurrentCore >= 0 && ((AffinityMask >> CurrentCore) & 1) == 0) + { + if (PreferredCore < 0) + { + CurrentCore = HighestSetCore(AffinityMask); + } + else + { + CurrentCore = PreferredCore; + } + } + + AdjustSchedulingForNewAffinity(OldAffinityMask, OldCore); + } + } + + System.CriticalSectionLock.Unlock(); + + return 0; + } + + private static int HighestSetCore(long Mask) + { + for (int Core = KScheduler.CpuCoresCount - 1; Core >= 0; Core--) + { + if (((Mask >> Core) & 1) != 0) + { + return Core; + } + } + + return -1; + } + + private void CombineForcePauseFlags() + { + ThreadSchedState OldFlags = SchedFlags; + ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask; + + SchedFlags = LowNibble | ForcePauseFlags; + + AdjustScheduling(OldFlags); + } + + private void SetNewSchedFlags(ThreadSchedState NewFlags) + { + System.CriticalSectionLock.Lock(); + + ThreadSchedState OldFlags = SchedFlags; + + SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | NewFlags; + + if ((OldFlags & ThreadSchedState.LowNibbleMask) != NewFlags) + { + AdjustScheduling(OldFlags); + } + + System.CriticalSectionLock.Unlock(); + } + + public void ReleaseAndResume() + { + System.CriticalSectionLock.Lock(); + + if ((SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused) + { + if (WithholderNode != null) + { + System.Withholders.Remove(WithholderNode); + + SetNewSchedFlags(ThreadSchedState.Running); + + WithholderNode = null; + } + else + { + SetNewSchedFlags(ThreadSchedState.Running); + } + } + + System.CriticalSectionLock.Unlock(); + } + + public void Reschedule(ThreadSchedState NewFlags) + { + System.CriticalSectionLock.Lock(); + + ThreadSchedState OldFlags = SchedFlags; + + SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | + (NewFlags & ThreadSchedState.LowNibbleMask); + + AdjustScheduling(OldFlags); + + System.CriticalSectionLock.Unlock(); + } + + public void AddMutexWaiter(KThread Requester) + { + AddToMutexWaitersList(Requester); + + Requester.MutexOwner = this; + + UpdatePriorityInheritance(); + } + + public void RemoveMutexWaiter(KThread Thread) + { + if (Thread.MutexWaiterNode?.List != null) + { + MutexWaiters.Remove(Thread.MutexWaiterNode); + } + + Thread.MutexOwner = null; + + UpdatePriorityInheritance(); + } + + public KThread RelinquishMutex(long MutexAddress, out int Count) + { + Count = 0; + + if (MutexWaiters.First == null) + { + return null; + } + + KThread NewMutexOwner = null; + + LinkedListNode CurrentNode = MutexWaiters.First; + + do + { + //Skip all threads that are not waiting for this mutex. + while (CurrentNode != null && CurrentNode.Value.MutexAddress != MutexAddress) + { + CurrentNode = CurrentNode.Next; + } + + if (CurrentNode == null) + { + break; + } + + LinkedListNode NextNode = CurrentNode.Next; + + MutexWaiters.Remove(CurrentNode); + + CurrentNode.Value.MutexOwner = NewMutexOwner; + + if (NewMutexOwner != null) + { + //New owner was already selected, re-insert on new owner list. + NewMutexOwner.AddToMutexWaitersList(CurrentNode.Value); + } + else + { + //New owner not selected yet, use current thread. + NewMutexOwner = CurrentNode.Value; + } + + Count++; + + CurrentNode = NextNode; + } + while (CurrentNode != null); + + if (NewMutexOwner != null) + { + UpdatePriorityInheritance(); + + NewMutexOwner.UpdatePriorityInheritance(); + } + + return NewMutexOwner; + } + + private void UpdatePriorityInheritance() + { + //If any of the threads waiting for the mutex has + //higher priority than the current thread, then + //the current thread inherits that priority. + int HighestPriority = BasePriority; + + if (MutexWaiters.First != null) + { + int WaitingDynamicPriority = MutexWaiters.First.Value.DynamicPriority; + + if (WaitingDynamicPriority < HighestPriority) + { + HighestPriority = WaitingDynamicPriority; + } + } + + if (HighestPriority != DynamicPriority) + { + int OldPriority = DynamicPriority; + + DynamicPriority = HighestPriority; + + AdjustSchedulingForNewPriority(OldPriority); + + if (MutexOwner != null) + { + //Remove and re-insert to ensure proper sorting based on new priority. + MutexOwner.MutexWaiters.Remove(MutexWaiterNode); + + MutexOwner.AddToMutexWaitersList(this); + + MutexOwner.UpdatePriorityInheritance(); + } + } + } + + private void AddToMutexWaitersList(KThread Thread) + { + LinkedListNode NextPrio = MutexWaiters.First; + + int CurrentPriority = Thread.DynamicPriority; + + while (NextPrio != null && NextPrio.Value.DynamicPriority <= CurrentPriority) + { + NextPrio = NextPrio.Next; + } + + if (NextPrio != null) + { + Thread.MutexWaiterNode = MutexWaiters.AddBefore(NextPrio, Thread); + } + else + { + Thread.MutexWaiterNode = MutexWaiters.AddLast(Thread); + } + } + + private void AdjustScheduling(ThreadSchedState OldFlags) + { + if (OldFlags == SchedFlags) + { + return; + } + + if (OldFlags == ThreadSchedState.Running) + { + //Was running, now it's stopped. + if (CurrentCore >= 0) + { + SchedulingData.Unschedule(DynamicPriority, CurrentCore, this); + } + + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + if (Core != CurrentCore && ((AffinityMask >> Core) & 1) != 0) + { + SchedulingData.Unsuggest(DynamicPriority, Core, this); + } + } + } + else if (SchedFlags == ThreadSchedState.Running) + { + //Was stopped, now it's running. + if (CurrentCore >= 0) + { + SchedulingData.Schedule(DynamicPriority, CurrentCore, this); + } + + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + if (Core != CurrentCore && ((AffinityMask >> Core) & 1) != 0) + { + SchedulingData.Suggest(DynamicPriority, Core, this); + } + } + } + + Scheduler.ThreadReselectionRequested = true; + } + + private void AdjustSchedulingForNewPriority(int OldPriority) + { + if (SchedFlags != ThreadSchedState.Running) + { + return; + } + + //Remove thread from the old priority queues. + if (CurrentCore >= 0) + { + SchedulingData.Unschedule(OldPriority, CurrentCore, this); + } + + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + if (Core != CurrentCore && ((AffinityMask >> Core) & 1) != 0) + { + SchedulingData.Unsuggest(OldPriority, Core, this); + } + } + + //Add thread to the new priority queues. + KThread CurrentThread = Scheduler.GetCurrentThread(); + + if (CurrentCore >= 0) + { + if (CurrentThread == this) + { + SchedulingData.SchedulePrepend(DynamicPriority, CurrentCore, this); + } + else + { + SchedulingData.Schedule(DynamicPriority, CurrentCore, this); + } + } + + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + if (Core != CurrentCore && ((AffinityMask >> Core) & 1) != 0) + { + SchedulingData.Suggest(DynamicPriority, Core, this); + } + } + + Scheduler.ThreadReselectionRequested = true; + } + + private void AdjustSchedulingForNewAffinity(long OldAffinityMask, int OldCore) + { + if (SchedFlags != ThreadSchedState.Running || DynamicPriority >= KScheduler.PrioritiesCount) + { + return; + } + + //Remove from old queues. + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + if (((OldAffinityMask >> Core) & 1) != 0) + { + if (Core == OldCore) + { + SchedulingData.Unschedule(DynamicPriority, Core, this); + } + else + { + SchedulingData.Unsuggest(DynamicPriority, Core, this); + } + } + } + + //Insert on new queues. + for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++) + { + if (((AffinityMask >> Core) & 1) != 0) + { + if (Core == CurrentCore) + { + SchedulingData.Schedule(DynamicPriority, Core, this); + } + else + { + SchedulingData.Suggest(DynamicPriority, Core, this); + } + } + } + + Scheduler.ThreadReselectionRequested = true; + } + + public override bool IsSignaled() + { + return HasExited; + } + + public void ClearExclusive() + { + Owner.Memory.ClearExclusive(CurrentCore); + } + + public void TimeUp() + { + System.CriticalSectionLock.Lock(); + + SetNewSchedFlags(ThreadSchedState.Running); + + System.CriticalSectionLock.Unlock(); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs new file mode 100644 index 0000000000..47a3c86cfd --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KTimeManager : IDisposable + { + private class WaitingObject + { + public IKFutureSchedulerObject Object { get; private set; } + + public long TimePoint { get; private set; } + + public WaitingObject(IKFutureSchedulerObject Object, long TimePoint) + { + this.Object = Object; + this.TimePoint = TimePoint; + } + } + + private List WaitingObjects; + + private AutoResetEvent WaitEvent; + + private Stopwatch Counter; + + private bool KeepRunning; + + public KTimeManager() + { + WaitingObjects = new List(); + + Counter = new Stopwatch(); + + Counter.Start(); + + KeepRunning = true; + + Thread Work = new Thread(WaitAndCheckScheduledObjects); + + Work.Start(); + } + + public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout) + { + lock (WaitingObjects) + { + long TimePoint = Counter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout); + + WaitingObjects.Add(new WaitingObject(Object, TimePoint)); + } + + WaitEvent.Set(); + } + + private long ConvertNanosecondsToMilliseconds(long Timeout) + { + Timeout /= 1000000; + + if ((ulong)Timeout > int.MaxValue) + { + return int.MaxValue; + } + + return Timeout; + } + + public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object) + { + lock (WaitingObjects) + { + WaitingObjects.RemoveAll(x => x.Object == Object); + } + } + + private void WaitAndCheckScheduledObjects() + { + using (WaitEvent = new AutoResetEvent(false)) + { + while (KeepRunning) + { + Monitor.Enter(WaitingObjects); + + WaitingObject Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault(); + + Monitor.Exit(WaitingObjects); + + if (Next != null) + { + long TimePoint = Counter.ElapsedMilliseconds; + + if (Next.TimePoint > TimePoint) + { + WaitEvent.WaitOne((int)(Next.TimePoint - TimePoint)); + } + + Monitor.Enter(WaitingObjects); + + bool TimeUp = Counter.ElapsedMilliseconds >= Next.TimePoint && WaitingObjects.Remove(Next); + + Monitor.Exit(WaitingObjects); + + if (TimeUp) + { + Next.Object.TimeUp(); + } + } + else + { + WaitEvent.WaitOne(); + } + } + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + KeepRunning = false; + + WaitEvent?.Set(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs b/Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs new file mode 100644 index 0000000000..1721ed0011 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KWritableEvent + { + private KEvent Parent; + + public KWritableEvent(KEvent Parent) + { + this.Parent = Parent; + } + + public void Signal() + { + Parent.ReadableEvent.Signal(); + } + + public KernelResult Clear() + { + return Parent.ReadableEvent.Clear(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelErr.cs b/Ryujinx.HLE/HOS/Kernel/KernelErr.cs index e76ae11e5f..e0b196f412 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelErr.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelErr.cs @@ -2,9 +2,11 @@ namespace Ryujinx.HLE.HOS.Kernel { static class KernelErr { + public const int ThreadTerminating = 59; public const int InvalidSize = 101; public const int InvalidAddress = 102; public const int OutOfMemory = 104; + public const int HandleTableFull = 105; public const int NoAccessPerm = 106; public const int InvalidPermission = 108; public const int InvalidMemRange = 110; @@ -13,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel public const int InvalidHandle = 114; public const int InvalidMaskValue = 116; public const int Timeout = 117; - public const int Canceled = 118; + public const int Cancelled = 118; public const int CountOutOfRange = 119; public const int InvalidEnumValue = 120; public const int InvalidThread = 122; diff --git a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs new file mode 100644 index 0000000000..d9cbfc6735 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum KernelResult + { + Success = 0, + HandleTableFull = 0xd201, + InvalidHandle = 0xe401, + InvalidState = 0xfa01 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/NsTimeConverter.cs b/Ryujinx.HLE/HOS/Kernel/NsTimeConverter.cs deleted file mode 100644 index b8008f7579..0000000000 --- a/Ryujinx.HLE/HOS/Kernel/NsTimeConverter.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Ryujinx.HLE.HOS.Kernel -{ - static class NsTimeConverter - { - public static int GetTimeMs(ulong Ns) - { - ulong Ms = Ns / 1_000_000; - - if (Ms < int.MaxValue) - { - return (int)Ms; - } - else - { - return int.MaxValue; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SchedulerThread.cs b/Ryujinx.HLE/HOS/Kernel/SchedulerThread.cs deleted file mode 100644 index bab7b03e61..0000000000 --- a/Ryujinx.HLE/HOS/Kernel/SchedulerThread.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Threading; - -namespace Ryujinx.HLE.HOS.Kernel -{ - class SchedulerThread : IDisposable - { - public KThread Thread { get; private set; } - - public SchedulerThread Next { get; set; } - - public bool IsActive { get; set; } - - public AutoResetEvent WaitSync { get; private set; } - public ManualResetEvent WaitActivity { get; private set; } - public AutoResetEvent WaitSched { get; private set; } - - public SchedulerThread(KThread Thread) - { - this.Thread = Thread; - - IsActive = true; - - WaitSync = new AutoResetEvent(false); - - WaitActivity = new ManualResetEvent(true); - - WaitSched = new AutoResetEvent(false); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - WaitSync.Dispose(); - - WaitActivity.Dispose(); - - WaitSched.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SignalType.cs b/Ryujinx.HLE/HOS/Kernel/SignalType.cs new file mode 100644 index 0000000000..0580315103 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/SignalType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum SignalType + { + Signal = 0, + SignalAndIncrementIfEqual = 1, + SignalAndModifyIfEqual = 2 + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs index fcb72c140e..9b475d4ebf 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs @@ -1,28 +1,43 @@ using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Threading; namespace Ryujinx.HLE.HOS.Kernel { partial class SvcHandler { - private delegate void SvcFunc(AThreadState ThreadState); + private delegate void SvcFunc(CpuThreadState ThreadState); private Dictionary SvcFuncs; - private Switch Device; - private Process Process; - private AMemory Memory; + private Switch Device; + private Process Process; + private Horizon System; + private MemoryManager Memory; - private ConcurrentDictionary SyncWaits; + private struct HleIpcMessage + { + public KThread Thread { get; private set; } + public KSession Session { get; private set; } + public IpcMessage Message { get; private set; } + public long MessagePtr { get; private set; } - private const uint SelfThreadHandle = 0xffff8000; - private const uint SelfProcessHandle = 0xffff8001; + public HleIpcMessage( + KThread Thread, + KSession Session, + IpcMessage Message, + long MessagePtr) + { + this.Thread = Thread; + this.Session = Session; + this.Message = Message; + this.MessagePtr = MessagePtr; + } + } private static Random Rng; @@ -45,12 +60,13 @@ namespace Ryujinx.HLE.HOS.Kernel { 0x0e, SvcGetThreadCoreMask }, { 0x0f, SvcSetThreadCoreMask }, { 0x10, SvcGetCurrentProcessorNumber }, - { 0x12, SvcClearEvent }, + { 0x11, SignalEvent64 }, + { 0x12, ClearEvent64 }, { 0x13, SvcMapSharedMemory }, { 0x14, SvcUnmapSharedMemory }, { 0x15, SvcCreateTransferMemory }, { 0x16, SvcCloseHandle }, - { 0x17, SvcResetSignal }, + { 0x17, ResetSignal64 }, { 0x18, SvcWaitSynchronization }, { 0x19, SvcCancelSynchronization }, { 0x1a, SvcArbitrateLock }, @@ -69,14 +85,15 @@ namespace Ryujinx.HLE.HOS.Kernel { 0x2d, SvcUnmapPhysicalMemory }, { 0x32, SvcSetThreadActivity }, { 0x33, SvcGetThreadContext3 }, - { 0x34, SvcWaitForAddress } + { 0x34, SvcWaitForAddress }, + { 0x35, SvcSignalToAddress }, + { 0x45, CreateEvent64 } }; this.Device = Device; this.Process = Process; + this.System = Process.Device.System; this.Memory = Process.Memory; - - SyncWaits = new ConcurrentDictionary(); } static SvcHandler() @@ -84,21 +101,19 @@ namespace Ryujinx.HLE.HOS.Kernel Rng = new Random(); } - public void SvcCall(object sender, AInstExceptionEventArgs e) + public void SvcCall(object sender, InstExceptionEventArgs e) { - AThreadState ThreadState = (AThreadState)sender; + CpuThreadState ThreadState = (CpuThreadState)sender; Process.GetThread(ThreadState.Tpidr).LastPc = e.Position; if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func)) { - Device.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called."); + Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called."); Func(ThreadState); - Process.Scheduler.Reschedule(Process.GetThread(ThreadState.Tpidr)); - - Device.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended."); + Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended."); } else { @@ -107,17 +122,5 @@ namespace Ryujinx.HLE.HOS.Kernel throw new NotImplementedException($"0x{e.Id:x4}"); } } - - private KThread GetThread(long Tpidr, int Handle) - { - if ((uint)Handle == SelfThreadHandle) - { - return Process.GetThread(Tpidr); - } - else - { - return Process.HandleTable.GetData(Handle); - } - } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs index b9e71b183c..560ad4b3f7 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs @@ -1,5 +1,5 @@ using ChocolArm64.State; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using static Ryujinx.HLE.HOS.ErrorCode; @@ -7,13 +7,13 @@ namespace Ryujinx.HLE.HOS.Kernel { partial class SvcHandler { - private void SvcSetHeapSize(AThreadState ThreadState) + private void SvcSetHeapSize(CpuThreadState ThreadState) { ulong Size = ThreadState.X1; if ((Size & 0xFFFFFFFE001FFFFF) != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -30,18 +30,18 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } } - private void SvcSetMemoryAttribute(AThreadState ThreadState) + private void SvcSetMemoryAttribute(CpuThreadState ThreadState) { long Position = (long)ThreadState.X0; long Size = (long)ThreadState.X1; if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Attributes != AttributeMask || (Attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!"); + Logger.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } else { @@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Kernel ThreadState.X0 = (ulong)Result; } - private void SvcMapMemory(AThreadState ThreadState) + private void SvcMapMemory(CpuThreadState ThreadState) { long Dst = (long)ThreadState.X0; long Src = (long)ThreadState.X1; @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Src | Dst)) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Src, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideNewMapRegion(Dst, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); @@ -145,13 +145,13 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; } - private void SvcUnmapMemory(AThreadState ThreadState) + private void SvcUnmapMemory(CpuThreadState ThreadState) { long Dst = (long)ThreadState.X0; long Src = (long)ThreadState.X1; @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Src | Dst)) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -177,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -186,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Src, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -195,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideNewMapRegion(Dst, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); @@ -206,13 +206,13 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; } - private void SvcQueryMemory(AThreadState ThreadState) + private void SvcQueryMemory(CpuThreadState ThreadState) { long InfoPtr = (long)ThreadState.X0; long Position = (long)ThreadState.X2; @@ -232,7 +232,7 @@ namespace Ryujinx.HLE.HOS.Kernel ThreadState.X1 = 0; } - private void SvcMapSharedMemory(AThreadState ThreadState) + private void SvcMapSharedMemory(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X0; long Position = (long)ThreadState.X1; @@ -240,7 +240,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -249,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -258,7 +258,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -269,18 +269,18 @@ namespace Ryujinx.HLE.HOS.Kernel if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); return; } - KSharedMemory SharedMemory = Process.HandleTable.GetData(Handle); + KSharedMemory SharedMemory = Process.HandleTable.GetObject(Handle); if (SharedMemory == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -289,7 +289,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -298,7 +298,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (SharedMemory.Size != Size) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -309,13 +309,13 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; } - private void SvcUnmapSharedMemory(AThreadState ThreadState) + private void SvcUnmapSharedMemory(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X0; long Position = (long)ThreadState.X1; @@ -323,7 +323,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -332,7 +332,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -341,18 +341,18 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } - KSharedMemory SharedMemory = Process.HandleTable.GetData(Handle); + KSharedMemory SharedMemory = Process.HandleTable.GetObject(Handle); if (SharedMemory == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -361,7 +361,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -372,20 +372,20 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; } - private void SvcCreateTransferMemory(AThreadState ThreadState) + private void SvcCreateTransferMemory(CpuThreadState ThreadState) { long Position = (long)ThreadState.X1; long Size = (long)ThreadState.X2; if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -394,7 +394,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -403,7 +403,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -414,7 +414,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Permission > MemoryPermission.ReadAndWrite || Permission == MemoryPermission.Write) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); @@ -425,20 +425,20 @@ namespace Ryujinx.HLE.HOS.Kernel KTransferMemory TransferMemory = new KTransferMemory(Position, Size); - int Handle = Process.HandleTable.OpenHandle(TransferMemory); + KernelResult Result = Process.HandleTable.GenerateHandle(TransferMemory, out int Handle); - ThreadState.X0 = 0; + ThreadState.X0 = (uint)Result; ThreadState.X1 = (ulong)Handle; } - private void SvcMapPhysicalMemory(AThreadState ThreadState) + private void SvcMapPhysicalMemory(CpuThreadState ThreadState) { long Position = (long)ThreadState.X0; long Size = (long)ThreadState.X1; if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -447,7 +447,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -456,7 +456,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -465,7 +465,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Position, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -476,20 +476,20 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; } - private void SvcUnmapPhysicalMemory(AThreadState ThreadState) + private void SvcUnmapPhysicalMemory(CpuThreadState ThreadState) { long Position = (long)ThreadState.X0; long Size = (long)ThreadState.X1; if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -498,7 +498,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -507,7 +507,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -516,7 +516,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Position, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -527,7 +527,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs index 7cc1c8588e..54aef5d708 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs @@ -1,9 +1,9 @@ using ChocolArm64.Memory; using ChocolArm64.State; +using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services; -using Ryujinx.HLE.Logging; using System; using System.Threading; @@ -17,29 +17,82 @@ namespace Ryujinx.HLE.HOS.Kernel private const bool EnableProcessDebugging = false; - private void SvcExitProcess(AThreadState ThreadState) + private void SvcExitProcess(CpuThreadState ThreadState) { Device.System.ExitProcess(Process.ProcessId); } - private void SvcClearEvent(AThreadState ThreadState) + private void SignalEvent64(CpuThreadState ThreadState) { - int Handle = (int)ThreadState.X0; - - //TODO: Implement events. - - ThreadState.X0 = 0; + ThreadState.X0 = (ulong)SignalEvent((int)ThreadState.X0); } - private void SvcCloseHandle(AThreadState ThreadState) + private KernelResult SignalEvent(int Handle) + { + KWritableEvent WritableEvent = Process.HandleTable.GetObject(Handle); + + KernelResult Result; + + if (WritableEvent != null) + { + WritableEvent.Signal(); + + Result = KernelResult.Success; + } + else + { + Result = KernelResult.InvalidHandle; + } + + if (Result != KernelResult.Success) + { + Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + } + + return Result; + } + + private void ClearEvent64(CpuThreadState ThreadState) + { + ThreadState.X0 = (ulong)ClearEvent((int)ThreadState.X0); + } + + private KernelResult ClearEvent(int Handle) + { + KernelResult Result; + + KWritableEvent WritableEvent = Process.HandleTable.GetObject(Handle); + + if (WritableEvent == null) + { + KReadableEvent ReadableEvent = Process.HandleTable.GetObject(Handle); + + Result = ReadableEvent?.Clear() ?? KernelResult.InvalidHandle; + } + else + { + Result = WritableEvent.Clear(); + } + + if (Result != KernelResult.Success) + { + Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + } + + return Result; + } + + private void SvcCloseHandle(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X0; - object Obj = Process.HandleTable.CloseHandle(Handle); + object Obj = Process.HandleTable.GetObject(Handle); + + Process.HandleTable.CloseHandle(Handle); if (Obj == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -60,163 +113,70 @@ namespace Ryujinx.HLE.HOS.Kernel ThreadState.X0 = 0; } - private void SvcResetSignal(AThreadState ThreadState) + private void ResetSignal64(CpuThreadState ThreadState) { - int Handle = (int)ThreadState.X0; + ThreadState.X0 = (ulong)ResetSignal((int)ThreadState.X0); + } - KEvent Event = Process.HandleTable.GetData(Handle); + private KernelResult ResetSignal(int Handle) + { + KReadableEvent ReadableEvent = Process.HandleTable.GetObject(Handle); - if (Event != null) + KernelResult Result; + + //TODO: KProcess support. + if (ReadableEvent != null) { - Event.WaitEvent.Reset(); - - ThreadState.X0 = 0; + Result = ReadableEvent.ClearIfSignaled(); } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid event handle 0x{Handle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + Result = KernelResult.InvalidHandle; } + + if (Result == KernelResult.InvalidState) + { + Logger.PrintDebug(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + } + else if (Result != KernelResult.Success) + { + Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + } + + return Result; } - private void SvcWaitSynchronization(AThreadState ThreadState) - { - long HandlesPtr = (long)ThreadState.X1; - int HandlesCount = (int)ThreadState.X2; - ulong Timeout = ThreadState.X3; - - Device.Log.PrintDebug(LogClass.KernelSvc, - "HandlesPtr = 0x" + HandlesPtr .ToString("x16") + ", " + - "HandlesCount = 0x" + HandlesCount.ToString("x8") + ", " + - "Timeout = 0x" + Timeout .ToString("x16")); - - if ((uint)HandlesCount > 0x40) - { - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange); - - return; - } - - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - WaitHandle[] Handles = new WaitHandle[HandlesCount + 1]; - - for (int Index = 0; Index < HandlesCount; Index++) - { - int Handle = Memory.ReadInt32(HandlesPtr + Index * 4); - - KSynchronizationObject SyncObj = Process.HandleTable.GetData(Handle); - - if (SyncObj == null) - { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - - return; - } - - Handles[Index] = SyncObj.WaitEvent; - } - - using (AutoResetEvent WaitEvent = new AutoResetEvent(false)) - { - if (!SyncWaits.TryAdd(CurrThread, WaitEvent)) - { - throw new InvalidOperationException(); - } - - Handles[HandlesCount] = WaitEvent; - - Process.Scheduler.Suspend(CurrThread); - - int HandleIndex; - - ulong Result = 0; - - if (Timeout != ulong.MaxValue) - { - HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout)); - } - else - { - HandleIndex = WaitHandle.WaitAny(Handles); - } - - if (HandleIndex == WaitHandle.WaitTimeout) - { - Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout); - } - else if (HandleIndex == HandlesCount) - { - Result = MakeError(ErrorModule.Kernel, KernelErr.Canceled); - } - - SyncWaits.TryRemove(CurrThread, out _); - - Process.Scheduler.Resume(CurrThread); - - ThreadState.X0 = Result; - - if (Result == 0) - { - ThreadState.X1 = (ulong)HandleIndex; - } - } - } - - private void SvcCancelSynchronization(AThreadState ThreadState) - { - int ThreadHandle = (int)ThreadState.X0; - - KThread Thread = GetThread(ThreadState.Tpidr, ThreadHandle); - - if (Thread == null) - { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - - return; - } - - if (SyncWaits.TryRemove(Thread, out AutoResetEvent WaitEvent)) - { - WaitEvent.Set(); - } - - ThreadState.X0 = 0; - } - - private void SvcGetSystemTick(AThreadState ThreadState) + private void SvcGetSystemTick(CpuThreadState ThreadState) { ThreadState.X0 = ThreadState.CntpctEl0; } - private void SvcConnectToNamedPort(AThreadState ThreadState) + private void SvcConnectToNamedPort(CpuThreadState ThreadState) { long StackPtr = (long)ThreadState.X0; long NamePtr = (long)ThreadState.X1; - string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8); + string Name = MemoryHelper.ReadAsciiString(Memory, NamePtr, 8); //TODO: Validate that app has perms to access the service, and that the service //actually exists, return error codes otherwise. - KSession Session = new KSession(ServiceFactory.MakeService(Name), Name); + KSession Session = new KSession(ServiceFactory.MakeService(System, Name), Name); - ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session); + if (Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } ThreadState.X0 = 0; - ThreadState.X1 = Handle; + ThreadState.X1 = (uint)Handle; } - private void SvcSendSyncRequest(AThreadState ThreadState) + private void SvcSendSyncRequest(CpuThreadState ThreadState) { SendSyncRequest(ThreadState, ThreadState.Tpidr, 0x100, (int)ThreadState.X0); } - private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState) + private void SvcSendSyncRequestWithUserBuffer(CpuThreadState ThreadState) { SendSyncRequest( ThreadState, @@ -225,60 +185,94 @@ namespace Ryujinx.HLE.HOS.Kernel (int)ThreadState.X2); } - private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle) + private void SendSyncRequest(CpuThreadState ThreadState, long MessagePtr, long Size, int Handle) { KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - byte[] CmdData = Memory.ReadBytes(CmdPtr, Size); + byte[] MessageData = Memory.ReadBytes(MessagePtr, Size); - KSession Session = Process.HandleTable.GetData(Handle); + KSession Session = Process.HandleTable.GetObject(Handle); if (Session != null) { - Process.Scheduler.Suspend(CurrThread); + //Process.Scheduler.Suspend(CurrThread); - IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); + System.CriticalSectionLock.Lock(); - long Result = IpcHandler.IpcCall(Device, Process, Memory, Session, Cmd, CmdPtr); + KThread CurrentThread = System.Scheduler.GetCurrentThread(); - Thread.Yield(); + CurrentThread.SignaledObj = null; + CurrentThread.ObjSyncResult = 0; - Process.Scheduler.Resume(CurrThread); + CurrentThread.Reschedule(ThreadSchedState.Paused); - ThreadState.X0 = (ulong)Result; + IpcMessage Message = new IpcMessage(MessageData, MessagePtr); + + ThreadPool.QueueUserWorkItem(ProcessIpcRequest, new HleIpcMessage( + CurrentThread, + Session, + Message, + MessagePtr)); + + System.CriticalSectionLock.Unlock(); + + ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult; } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } } - private void SvcBreak(AThreadState ThreadState) + private void ProcessIpcRequest(object State) + { + HleIpcMessage IpcMessage = (HleIpcMessage)State; + + IpcMessage.Thread.ObjSyncResult = (int)IpcHandler.IpcCall( + Device, + Process, + Memory, + IpcMessage.Session, + IpcMessage.Message, + IpcMessage.MessagePtr); + + IpcMessage.Thread.Reschedule(ThreadSchedState.Running); + } + + private void SvcBreak(CpuThreadState ThreadState) { long Reason = (long)ThreadState.X0; long Unknown = (long)ThreadState.X1; long Info = (long)ThreadState.X2; - Process.PrintStackTrace(ThreadState); + if ((Reason & (1 << 31)) == 0) + { + Process.PrintStackTrace(ThreadState); - throw new GuestBrokeExecutionException(); + throw new GuestBrokeExecutionException(); + } + else + { + Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered"); + Process.PrintStackTrace(ThreadState); + } } - private void SvcOutputDebugString(AThreadState ThreadState) + private void SvcOutputDebugString(CpuThreadState ThreadState) { long Position = (long)ThreadState.X0; long Size = (long)ThreadState.X1; - string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size); + string Str = MemoryHelper.ReadAsciiString(Memory, Position, Size); - Device.Log.PrintWarning(LogClass.KernelSvc, Str); + Logger.PrintWarning(LogClass.KernelSvc, Str); ThreadState.X0 = 0; } - private void SvcGetInfo(AThreadState ThreadState) + private void SvcGetInfo(CpuThreadState ThreadState) { long StackPtr = (long)ThreadState.X0; int InfoType = (int)ThreadState.X1; @@ -288,7 +282,9 @@ namespace Ryujinx.HLE.HOS.Kernel //Fail for info not available on older Kernel versions. if (InfoType == 18 || InfoType == 19 || - InfoType == 20) + InfoType == 20 || + InfoType == 21 || + InfoType == 22) { ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue); @@ -369,5 +365,37 @@ namespace Ryujinx.HLE.HOS.Kernel ThreadState.X0 = 0; } + + private void CreateEvent64(CpuThreadState State) + { + KernelResult Result = CreateEvent(out int WEventHandle, out int REventHandle); + + State.X0 = (ulong)Result; + State.X1 = (ulong)WEventHandle; + State.X2 = (ulong)REventHandle; + } + + private KernelResult CreateEvent(out int WEventHandle, out int REventHandle) + { + KEvent Event = new KEvent(System); + + KernelResult Result = Process.HandleTable.GenerateHandle(Event.WritableEvent, out WEventHandle); + + if (Result == KernelResult.Success) + { + Result = Process.HandleTable.GenerateHandle(Event.ReadableEvent, out REventHandle); + + if (Result != KernelResult.Success) + { + Process.HandleTable.CloseHandle(WEventHandle); + } + } + else + { + REventHandle = 0; + } + + return Result; + } } } diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs index 69e75ec0af..53a557de5e 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs @@ -1,6 +1,5 @@ using ChocolArm64.State; -using Ryujinx.HLE.Logging; -using System.Threading; +using Ryujinx.Common.Logging; using static Ryujinx.HLE.HOS.ErrorCode; @@ -8,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel { partial class SvcHandler { - private void SvcCreateThread(AThreadState ThreadState) + private void SvcCreateThread(CpuThreadState ThreadState) { long EntryPoint = (long)ThreadState.X1; long ArgsPtr = (long)ThreadState.X2; @@ -18,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((uint)Priority > 0x3f) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority); @@ -32,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else if ((uint)ProcessorId > 3) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId); @@ -50,158 +49,169 @@ namespace Ryujinx.HLE.HOS.Kernel ThreadState.X1 = (ulong)Handle; } - private void SvcStartThread(AThreadState ThreadState) + private void SvcStartThread(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X0; - KThread NewThread = Process.HandleTable.GetData(Handle); + KThread Thread = Process.HandleTable.GetObject(Handle); - if (NewThread != null) + if (Thread != null) { - Process.Scheduler.StartThread(NewThread); - Process.Scheduler.SetReschedule(NewThread.ProcessorId); + long Result = Thread.Start(); - ThreadState.X0 = 0; + if (Result != 0) + { + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } } - private void SvcExitThread(AThreadState ThreadState) + private void SvcExitThread(CpuThreadState ThreadState) { - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + KThread CurrentThread = System.Scheduler.GetCurrentThread(); - CurrThread.Thread.StopExecution(); + CurrentThread.Exit(); + + System.Scheduler.StopThread(CurrentThread); } - private void SvcSleepThread(AThreadState ThreadState) + private void SvcSleepThread(CpuThreadState ThreadState) { - ulong TimeoutNs = ThreadState.X0; + long Timeout = (long)ThreadState.X0; - Device.Log.PrintDebug(LogClass.KernelSvc, "Timeout = 0x" + TimeoutNs.ToString("x16")); + Logger.PrintDebug(LogClass.KernelSvc, "Timeout = 0x" + Timeout.ToString("x16")); - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); + KThread CurrentThread = System.Scheduler.GetCurrentThread(); - if (TimeoutNs == 0 || TimeoutNs == ulong.MaxValue) + if (Timeout < 1) { - Process.Scheduler.Yield(CurrThread); + switch (Timeout) + { + case 0: CurrentThread.Yield(); break; + case -1: CurrentThread.YieldWithLoadBalancing(); break; + case -2: CurrentThread.YieldAndWaitForLoadBalancing(); break; + } } else { - Process.Scheduler.Suspend(CurrThread); + CurrentThread.Sleep(Timeout); - Thread.Sleep(NsTimeConverter.GetTimeMs(TimeoutNs)); - - Process.Scheduler.Resume(CurrThread); + ThreadState.X0 = 0; } } - private void SvcGetThreadPriority(AThreadState ThreadState) + private void SvcGetThreadPriority(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X1; - KThread Thread = GetThread(ThreadState.Tpidr, Handle); + KThread Thread = Process.HandleTable.GetKThread(Handle); if (Thread != null) { ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Thread.ActualPriority; + ThreadState.X1 = (ulong)Thread.DynamicPriority; } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } } - private void SvcSetThreadPriority(AThreadState ThreadState) + private void SvcSetThreadPriority(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X0; int Priority = (int)ThreadState.X1; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle .ToString("x8") + ", " + "Priority = 0x" + Priority.ToString("x8")); - KThread Thread = GetThread(ThreadState.Tpidr, Handle); + //TODO: NPDM check. - if (Thread != null) - { - Thread.SetPriority(Priority); + KThread Thread = Process.HandleTable.GetKThread(Handle); - ThreadState.X0 = 0; - } - else + if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; } + + Thread.SetPriority(Priority); + + ThreadState.X0 = 0; } - private void SvcGetThreadCoreMask(AThreadState ThreadState) + private void SvcGetThreadCoreMask(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X2; - Device.Log.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle.ToString("x8")); + Logger.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle.ToString("x8")); - KThread Thread = GetThread(ThreadState.Tpidr, Handle); + KThread Thread = Process.HandleTable.GetKThread(Handle); if (Thread != null) { ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Thread.IdealCore; - ThreadState.X2 = (ulong)Thread.CoreMask; + ThreadState.X1 = (ulong)Thread.PreferredCore; + ThreadState.X2 = (ulong)Thread.AffinityMask; } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } } - private void SvcSetThreadCoreMask(AThreadState ThreadState) + private void SvcSetThreadCoreMask(CpuThreadState ThreadState) { - int Handle = (int)ThreadState.X0; - int IdealCore = (int)ThreadState.X1; - long CoreMask = (long)ThreadState.X2; + int Handle = (int)ThreadState.X0; + int PrefferedCore = (int)ThreadState.X1; + long AffinityMask = (long)ThreadState.X2; - Device.Log.PrintDebug(LogClass.KernelSvc, - "Handle = 0x" + Handle .ToString("x8") + ", " + - "IdealCore = 0x" + IdealCore.ToString("x8") + ", " + - "CoreMask = 0x" + CoreMask .ToString("x16")); + Logger.PrintDebug(LogClass.KernelSvc, + "Handle = 0x" + Handle .ToString("x8") + ", " + + "PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " + + "AffinityMask = 0x" + AffinityMask .ToString("x16")); - KThread Thread = GetThread(ThreadState.Tpidr, Handle); - - if (IdealCore == -2) + if (PrefferedCore == -2) { //TODO: Get this value from the NPDM file. - IdealCore = 0; + PrefferedCore = 0; - CoreMask = 1 << IdealCore; + AffinityMask = 1 << PrefferedCore; } else { - if ((uint)IdealCore > 3) + //TODO: Check allowed cores from NPDM file. + + if ((uint)PrefferedCore > 3) { - if ((IdealCore | 2) != -1) + if ((PrefferedCore | 2) != -1) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{IdealCore:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId); return; } } - else if ((CoreMask & (1 << IdealCore)) == 0) + else if ((AffinityMask & (1 << PrefferedCore)) == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); @@ -209,42 +219,37 @@ namespace Ryujinx.HLE.HOS.Kernel } } + KThread Thread = Process.HandleTable.GetKThread(Handle); + if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); return; } - //-1 is used as "don't care", so the IdealCore value is ignored. - //-2 is used as "use NPDM default core id" (handled above). - //-3 is used as "don't update", the old IdealCore value is kept. - if (IdealCore == -3 && (CoreMask & (1 << Thread.IdealCore)) == 0) + long Result = Thread.SetCoreAndAffinityMask(PrefferedCore, AffinityMask); + + if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); - - return; + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - Process.Scheduler.ChangeCore(Thread, IdealCore, (int)CoreMask); - - ThreadState.X0 = 0; + ThreadState.X0 = (ulong)Result; } - private void SvcGetCurrentProcessorNumber(AThreadState ThreadState) + private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState) { - ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).ActualCore; + ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).CurrentCore; } - private void SvcGetThreadId(AThreadState ThreadState) + private void SvcGetThreadId(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X1; - KThread Thread = GetThread(ThreadState.Tpidr, Handle); + KThread Thread = Process.HandleTable.GetKThread(Handle); if (Thread != null) { @@ -253,43 +258,57 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } } - private void SvcSetThreadActivity(AThreadState ThreadState) + private void SvcSetThreadActivity(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X0; - bool Active = (int)ThreadState.X1 == 0; + bool Pause = (int)ThreadState.X1 == 1; - KThread Thread = Process.HandleTable.GetData(Handle); + KThread Thread = Process.HandleTable.GetObject(Handle); - if (Thread != null) + if (Thread == null) { - Process.Scheduler.SetThreadActivity(Thread, Active); - - ThreadState.X0 = 0; - } - else - { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; } + + if (Thread.Owner != Process) + { + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + long Result = Thread.SetActivity(Pause); + + if (Result != 0) + { + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } - private void SvcGetThreadContext3(AThreadState ThreadState) + private void SvcGetThreadContext3(CpuThreadState ThreadState) { long Position = (long)ThreadState.X0; int Handle = (int)ThreadState.X1; - KThread Thread = Process.HandleTable.GetData(Handle); + KThread Thread = Process.HandleTable.GetObject(Handle); if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -298,86 +317,86 @@ namespace Ryujinx.HLE.HOS.Kernel if (Process.GetThread(ThreadState.Tpidr) == Thread) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread); return; } - Memory.WriteUInt64(Position + 0x0, ThreadState.X0); - Memory.WriteUInt64(Position + 0x8, ThreadState.X1); - Memory.WriteUInt64(Position + 0x10, ThreadState.X2); - Memory.WriteUInt64(Position + 0x18, ThreadState.X3); - Memory.WriteUInt64(Position + 0x20, ThreadState.X4); - Memory.WriteUInt64(Position + 0x28, ThreadState.X5); - Memory.WriteUInt64(Position + 0x30, ThreadState.X6); - Memory.WriteUInt64(Position + 0x38, ThreadState.X7); - Memory.WriteUInt64(Position + 0x40, ThreadState.X8); - Memory.WriteUInt64(Position + 0x48, ThreadState.X9); - Memory.WriteUInt64(Position + 0x50, ThreadState.X10); - Memory.WriteUInt64(Position + 0x58, ThreadState.X11); - Memory.WriteUInt64(Position + 0x60, ThreadState.X12); - Memory.WriteUInt64(Position + 0x68, ThreadState.X13); - Memory.WriteUInt64(Position + 0x70, ThreadState.X14); - Memory.WriteUInt64(Position + 0x78, ThreadState.X15); - Memory.WriteUInt64(Position + 0x80, ThreadState.X16); - Memory.WriteUInt64(Position + 0x88, ThreadState.X17); - Memory.WriteUInt64(Position + 0x90, ThreadState.X18); - Memory.WriteUInt64(Position + 0x98, ThreadState.X19); - Memory.WriteUInt64(Position + 0xa0, ThreadState.X20); - Memory.WriteUInt64(Position + 0xa8, ThreadState.X21); - Memory.WriteUInt64(Position + 0xb0, ThreadState.X22); - Memory.WriteUInt64(Position + 0xb8, ThreadState.X23); - Memory.WriteUInt64(Position + 0xc0, ThreadState.X24); - Memory.WriteUInt64(Position + 0xc8, ThreadState.X25); - Memory.WriteUInt64(Position + 0xd0, ThreadState.X26); - Memory.WriteUInt64(Position + 0xd8, ThreadState.X27); - Memory.WriteUInt64(Position + 0xe0, ThreadState.X28); - Memory.WriteUInt64(Position + 0xe8, ThreadState.X29); - Memory.WriteUInt64(Position + 0xf0, ThreadState.X30); - Memory.WriteUInt64(Position + 0xf8, ThreadState.X31); + Memory.WriteUInt64(Position + 0x0, Thread.Context.ThreadState.X0); + Memory.WriteUInt64(Position + 0x8, Thread.Context.ThreadState.X1); + Memory.WriteUInt64(Position + 0x10, Thread.Context.ThreadState.X2); + Memory.WriteUInt64(Position + 0x18, Thread.Context.ThreadState.X3); + Memory.WriteUInt64(Position + 0x20, Thread.Context.ThreadState.X4); + Memory.WriteUInt64(Position + 0x28, Thread.Context.ThreadState.X5); + Memory.WriteUInt64(Position + 0x30, Thread.Context.ThreadState.X6); + Memory.WriteUInt64(Position + 0x38, Thread.Context.ThreadState.X7); + Memory.WriteUInt64(Position + 0x40, Thread.Context.ThreadState.X8); + Memory.WriteUInt64(Position + 0x48, Thread.Context.ThreadState.X9); + Memory.WriteUInt64(Position + 0x50, Thread.Context.ThreadState.X10); + Memory.WriteUInt64(Position + 0x58, Thread.Context.ThreadState.X11); + Memory.WriteUInt64(Position + 0x60, Thread.Context.ThreadState.X12); + Memory.WriteUInt64(Position + 0x68, Thread.Context.ThreadState.X13); + Memory.WriteUInt64(Position + 0x70, Thread.Context.ThreadState.X14); + Memory.WriteUInt64(Position + 0x78, Thread.Context.ThreadState.X15); + Memory.WriteUInt64(Position + 0x80, Thread.Context.ThreadState.X16); + Memory.WriteUInt64(Position + 0x88, Thread.Context.ThreadState.X17); + Memory.WriteUInt64(Position + 0x90, Thread.Context.ThreadState.X18); + Memory.WriteUInt64(Position + 0x98, Thread.Context.ThreadState.X19); + Memory.WriteUInt64(Position + 0xa0, Thread.Context.ThreadState.X20); + Memory.WriteUInt64(Position + 0xa8, Thread.Context.ThreadState.X21); + Memory.WriteUInt64(Position + 0xb0, Thread.Context.ThreadState.X22); + Memory.WriteUInt64(Position + 0xb8, Thread.Context.ThreadState.X23); + Memory.WriteUInt64(Position + 0xc0, Thread.Context.ThreadState.X24); + Memory.WriteUInt64(Position + 0xc8, Thread.Context.ThreadState.X25); + Memory.WriteUInt64(Position + 0xd0, Thread.Context.ThreadState.X26); + Memory.WriteUInt64(Position + 0xd8, Thread.Context.ThreadState.X27); + Memory.WriteUInt64(Position + 0xe0, Thread.Context.ThreadState.X28); + Memory.WriteUInt64(Position + 0xe8, Thread.Context.ThreadState.X29); + Memory.WriteUInt64(Position + 0xf0, Thread.Context.ThreadState.X30); + Memory.WriteUInt64(Position + 0xf8, Thread.Context.ThreadState.X31); Memory.WriteInt64(Position + 0x100, Thread.LastPc); - Memory.WriteUInt64(Position + 0x108, (ulong)ThreadState.Psr); + Memory.WriteUInt64(Position + 0x108, (ulong)Thread.Context.ThreadState.Psr); - Memory.WriteVector128(Position + 0x110, ThreadState.V0); - Memory.WriteVector128(Position + 0x120, ThreadState.V1); - Memory.WriteVector128(Position + 0x130, ThreadState.V2); - Memory.WriteVector128(Position + 0x140, ThreadState.V3); - Memory.WriteVector128(Position + 0x150, ThreadState.V4); - Memory.WriteVector128(Position + 0x160, ThreadState.V5); - Memory.WriteVector128(Position + 0x170, ThreadState.V6); - Memory.WriteVector128(Position + 0x180, ThreadState.V7); - Memory.WriteVector128(Position + 0x190, ThreadState.V8); - Memory.WriteVector128(Position + 0x1a0, ThreadState.V9); - Memory.WriteVector128(Position + 0x1b0, ThreadState.V10); - Memory.WriteVector128(Position + 0x1c0, ThreadState.V11); - Memory.WriteVector128(Position + 0x1d0, ThreadState.V12); - Memory.WriteVector128(Position + 0x1e0, ThreadState.V13); - Memory.WriteVector128(Position + 0x1f0, ThreadState.V14); - Memory.WriteVector128(Position + 0x200, ThreadState.V15); - Memory.WriteVector128(Position + 0x210, ThreadState.V16); - Memory.WriteVector128(Position + 0x220, ThreadState.V17); - Memory.WriteVector128(Position + 0x230, ThreadState.V18); - Memory.WriteVector128(Position + 0x240, ThreadState.V19); - Memory.WriteVector128(Position + 0x250, ThreadState.V20); - Memory.WriteVector128(Position + 0x260, ThreadState.V21); - Memory.WriteVector128(Position + 0x270, ThreadState.V22); - Memory.WriteVector128(Position + 0x280, ThreadState.V23); - Memory.WriteVector128(Position + 0x290, ThreadState.V24); - Memory.WriteVector128(Position + 0x2a0, ThreadState.V25); - Memory.WriteVector128(Position + 0x2b0, ThreadState.V26); - Memory.WriteVector128(Position + 0x2c0, ThreadState.V27); - Memory.WriteVector128(Position + 0x2d0, ThreadState.V28); - Memory.WriteVector128(Position + 0x2e0, ThreadState.V29); - Memory.WriteVector128(Position + 0x2f0, ThreadState.V30); - Memory.WriteVector128(Position + 0x300, ThreadState.V31); + Memory.WriteVector128(Position + 0x110, Thread.Context.ThreadState.V0); + Memory.WriteVector128(Position + 0x120, Thread.Context.ThreadState.V1); + Memory.WriteVector128(Position + 0x130, Thread.Context.ThreadState.V2); + Memory.WriteVector128(Position + 0x140, Thread.Context.ThreadState.V3); + Memory.WriteVector128(Position + 0x150, Thread.Context.ThreadState.V4); + Memory.WriteVector128(Position + 0x160, Thread.Context.ThreadState.V5); + Memory.WriteVector128(Position + 0x170, Thread.Context.ThreadState.V6); + Memory.WriteVector128(Position + 0x180, Thread.Context.ThreadState.V7); + Memory.WriteVector128(Position + 0x190, Thread.Context.ThreadState.V8); + Memory.WriteVector128(Position + 0x1a0, Thread.Context.ThreadState.V9); + Memory.WriteVector128(Position + 0x1b0, Thread.Context.ThreadState.V10); + Memory.WriteVector128(Position + 0x1c0, Thread.Context.ThreadState.V11); + Memory.WriteVector128(Position + 0x1d0, Thread.Context.ThreadState.V12); + Memory.WriteVector128(Position + 0x1e0, Thread.Context.ThreadState.V13); + Memory.WriteVector128(Position + 0x1f0, Thread.Context.ThreadState.V14); + Memory.WriteVector128(Position + 0x200, Thread.Context.ThreadState.V15); + Memory.WriteVector128(Position + 0x210, Thread.Context.ThreadState.V16); + Memory.WriteVector128(Position + 0x220, Thread.Context.ThreadState.V17); + Memory.WriteVector128(Position + 0x230, Thread.Context.ThreadState.V18); + Memory.WriteVector128(Position + 0x240, Thread.Context.ThreadState.V19); + Memory.WriteVector128(Position + 0x250, Thread.Context.ThreadState.V20); + Memory.WriteVector128(Position + 0x260, Thread.Context.ThreadState.V21); + Memory.WriteVector128(Position + 0x270, Thread.Context.ThreadState.V22); + Memory.WriteVector128(Position + 0x280, Thread.Context.ThreadState.V23); + Memory.WriteVector128(Position + 0x290, Thread.Context.ThreadState.V24); + Memory.WriteVector128(Position + 0x2a0, Thread.Context.ThreadState.V25); + Memory.WriteVector128(Position + 0x2b0, Thread.Context.ThreadState.V26); + Memory.WriteVector128(Position + 0x2c0, Thread.Context.ThreadState.V27); + Memory.WriteVector128(Position + 0x2d0, Thread.Context.ThreadState.V28); + Memory.WriteVector128(Position + 0x2e0, Thread.Context.ThreadState.V29); + Memory.WriteVector128(Position + 0x2f0, Thread.Context.ThreadState.V30); + Memory.WriteVector128(Position + 0x300, Thread.Context.ThreadState.V31); - Memory.WriteInt32(Position + 0x310, ThreadState.Fpcr); - Memory.WriteInt32(Position + 0x314, ThreadState.Fpsr); - Memory.WriteInt64(Position + 0x318, ThreadState.Tpidr); + Memory.WriteInt32(Position + 0x310, Thread.Context.ThreadState.Fpcr); + Memory.WriteInt32(Position + 0x314, Thread.Context.ThreadState.Fpsr); + Memory.WriteInt64(Position + 0x318, Thread.Context.ThreadState.Tpidr); ThreadState.X0 = 0; } diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs index 7097d0f71a..318bd290f9 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs @@ -1,6 +1,6 @@ using ChocolArm64.State; -using Ryujinx.HLE.Logging; -using System; +using Ryujinx.Common.Logging; +using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -8,22 +8,99 @@ namespace Ryujinx.HLE.HOS.Kernel { partial class SvcHandler { - private const int MutexHasListenersMask = 0x40000000; - - private void SvcArbitrateLock(AThreadState ThreadState) + private void SvcWaitSynchronization(CpuThreadState ThreadState) { - int OwnerThreadHandle = (int)ThreadState.X0; - long MutexAddress = (long)ThreadState.X1; - int WaitThreadHandle = (int)ThreadState.X2; + long HandlesPtr = (long)ThreadState.X1; + int HandlesCount = (int)ThreadState.X2; + long Timeout = (long)ThreadState.X3; - Device.Log.PrintDebug(LogClass.KernelSvc, - "OwnerThreadHandle = 0x" + OwnerThreadHandle.ToString("x8") + ", " + - "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " + - "WaitThreadHandle = 0x" + WaitThreadHandle .ToString("x8")); + Logger.PrintDebug(LogClass.KernelSvc, + "HandlesPtr = 0x" + HandlesPtr .ToString("x16") + ", " + + "HandlesCount = 0x" + HandlesCount.ToString("x8") + ", " + + "Timeout = 0x" + Timeout .ToString("x16")); + + if ((uint)HandlesCount > 0x40) + { + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange); + + return; + } + + List SyncObjs = new List(); + + for (int Index = 0; Index < HandlesCount; Index++) + { + int Handle = Memory.ReadInt32(HandlesPtr + Index * 4); + + KSynchronizationObject SyncObj = Process.HandleTable.GetObject(Handle); + + if (SyncObj == null) + { + break; + } + + SyncObjs.Add(SyncObj); + } + + int HndIndex = (int)ThreadState.X1; + + ulong High = ThreadState.X1 & (0xffffffffUL << 32); + + long Result = System.Synchronization.WaitFor(SyncObjs.ToArray(), Timeout, ref HndIndex); + + if (Result != 0) + { + if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout) || + Result == MakeError(ErrorModule.Kernel, KernelErr.Cancelled)) + { + Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + else + { + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + } + + ThreadState.X0 = (ulong)Result; + ThreadState.X1 = (uint)HndIndex | High; + } + + private void SvcCancelSynchronization(CpuThreadState ThreadState) + { + int ThreadHandle = (int)ThreadState.X0; + + Logger.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8")); + + KThread Thread = Process.HandleTable.GetKThread(ThreadHandle); + + if (Thread == null) + { + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + Thread.CancelSynchronization(); + + ThreadState.X0 = 0; + } + + private void SvcArbitrateLock(CpuThreadState ThreadState) + { + int OwnerHandle = (int)ThreadState.X0; + long MutexAddress = (long)ThreadState.X1; + int RequesterHandle = (int)ThreadState.X2; + + Logger.PrintDebug(LogClass.KernelSvc, + "OwnerHandle = 0x" + OwnerHandle .ToString("x8") + ", " + + "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " + + "RequesterHandle = 0x" + RequesterHandle.ToString("x8")); if (IsPointingInsideKernel(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -32,51 +109,37 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - KThread OwnerThread = Process.HandleTable.GetData(OwnerThreadHandle); + long Result = System.AddressArbiter.ArbitrateLock( + Process, + Memory, + OwnerHandle, + MutexAddress, + RequesterHandle); - if (OwnerThread == null) + if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - - return; + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - KThread WaitThread = Process.HandleTable.GetData(WaitThreadHandle); - - if (WaitThread == null) - { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid requesting thread handle 0x{WaitThreadHandle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - - return; - } - - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - MutexLock(CurrThread, WaitThread, OwnerThreadHandle, WaitThreadHandle, MutexAddress); - - ThreadState.X0 = 0; + ThreadState.X0 = (ulong)Result; } - private void SvcArbitrateUnlock(AThreadState ThreadState) + private void SvcArbitrateUnlock(CpuThreadState ThreadState) { long MutexAddress = (long)ThreadState.X0; - Device.Log.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress.ToString("x16")); + Logger.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress.ToString("x16")); if (IsPointingInsideKernel(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -85,26 +148,31 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress); + long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress); - ThreadState.X0 = 0; + if (Result != 0) + { + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } - private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState) + private void SvcWaitProcessWideKeyAtomic(CpuThreadState ThreadState) { long MutexAddress = (long)ThreadState.X0; long CondVarAddress = (long)ThreadState.X1; int ThreadHandle = (int)ThreadState.X2; - ulong Timeout = ThreadState.X3; + long Timeout = (long)ThreadState.X3; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " + "CondVarAddress = 0x" + CondVarAddress.ToString("x16") + ", " + "ThreadHandle = 0x" + ThreadHandle .ToString("x8") + ", " + @@ -112,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsPointingInsideKernel(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -121,97 +189,65 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } - KThread Thread = Process.HandleTable.GetData(ThreadHandle); + long Result = System.AddressArbiter.WaitProcessWideKeyAtomic( + Memory, + MutexAddress, + CondVarAddress, + ThreadHandle, + Timeout); - if (Thread == null) + if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); - - return; - } - - KThread WaitThread = Process.GetThread(ThreadState.Tpidr); - - if (!CondVarWait(WaitThread, ThreadHandle, MutexAddress, CondVarAddress, Timeout)) - { - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout); - - return; - } - - ThreadState.X0 = 0; - } - - private void SvcSignalProcessWideKey(AThreadState ThreadState) - { - long CondVarAddress = (long)ThreadState.X0; - int Count = (int)ThreadState.X1; - - Device.Log.PrintDebug(LogClass.KernelSvc, - "CondVarAddress = 0x" + CondVarAddress.ToString("x16") + ", " + - "Count = 0x" + Count .ToString("x8")); - - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - - CondVarSignal(ThreadState, CurrThread, CondVarAddress, Count); - - ThreadState.X0 = 0; - } - - private void MutexLock( - KThread CurrThread, - KThread WaitThread, - int OwnerThreadHandle, - int WaitThreadHandle, - long MutexAddress) - { - lock (Process.ThreadSyncLock) - { - int MutexValue = Memory.ReadInt32(MutexAddress); - - Device.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = 0x" + MutexValue.ToString("x8")); - - if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask)) + if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout)) { - return; + Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + else + { + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - - CurrThread.WaitHandle = WaitThreadHandle; - CurrThread.MutexAddress = MutexAddress; - - InsertWaitingMutexThreadUnsafe(OwnerThreadHandle, WaitThread); } - Device.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state..."); - - Process.Scheduler.EnterWait(CurrThread); + ThreadState.X0 = (ulong)Result; } - private void SvcWaitForAddress(AThreadState ThreadState) + private void SvcSignalProcessWideKey(CpuThreadState ThreadState) { - long Address = (long)ThreadState.X0; - ArbitrationType Type = (ArbitrationType)ThreadState.X1; - int Value = (int)ThreadState.X2; - ulong Timeout = ThreadState.X3; + long Address = (long)ThreadState.X0; + int Count = (int)ThreadState.X1; - Device.Log.PrintDebug(LogClass.KernelSvc, - "Address = 0x" + Address.ToString("x16") + ", " + - "ArbitrationType = 0x" + Type .ToString() + ", " + - "Value = 0x" + Value .ToString("x8") + ", " + - "Timeout = 0x" + Timeout.ToString("x16")); + Logger.PrintDebug(LogClass.KernelSvc, + "Address = 0x" + Address.ToString("x16") + ", " + + "Count = 0x" + Count .ToString("x8")); + + System.AddressArbiter.SignalProcessWideKey(Process, Memory, Address, Count); + + ThreadState.X0 = 0; + } + + private void SvcWaitForAddress(CpuThreadState ThreadState) + { + long Address = (long)ThreadState.X0; + ArbitrationType Type = (ArbitrationType)ThreadState.X1; + int Value = (int)ThreadState.X2; + long Timeout = (long)ThreadState.X3; + + Logger.PrintDebug(LogClass.KernelSvc, + "Address = 0x" + Address.ToString("x16") + ", " + + "Type = " + Type .ToString() + ", " + + "Value = 0x" + Value .ToString("x8") + ", " + + "Timeout = 0x" + Timeout.ToString("x16")); if (IsPointingInsideKernel(Address)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -220,294 +256,100 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(Address)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); return; } + long Result; + switch (Type) { case ArbitrationType.WaitIfLessThan: - ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, false); + Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout); break; case ArbitrationType.DecrementAndWaitIfLessThan: - ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, true); + Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout); break; case ArbitrationType.WaitIfEqual: - ThreadState.X0 = AddressArbiter.WaitForAddressIfEqual(Process, ThreadState, Memory, Address, Value, Timeout); + Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout); break; default: - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue); + Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue); break; } + + if (Result != 0) + { + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + } + + ThreadState.X0 = (ulong)Result; } - private void MutexUnlock(KThread CurrThread, long MutexAddress) + private void SvcSignalToAddress(CpuThreadState ThreadState) { - lock (Process.ThreadSyncLock) + long Address = (long)ThreadState.X0; + SignalType Type = (SignalType)ThreadState.X1; + int Value = (int)ThreadState.X2; + int Count = (int)ThreadState.X3; + + Logger.PrintDebug(LogClass.KernelSvc, + "Address = 0x" + Address.ToString("x16") + ", " + + "Type = " + Type .ToString() + ", " + + "Value = 0x" + Value .ToString("x8") + ", " + + "Count = 0x" + Count .ToString("x8")); + + if (IsPointingInsideKernel(Address)) { - //This is the new thread that will now own the mutex. - //If no threads are waiting for the lock, then it should be null. - (KThread OwnerThread, int Count) = PopMutexThreadUnsafe(CurrThread, MutexAddress); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); - if (OwnerThread == CurrThread) - { - throw new InvalidOperationException(); - } - - if (OwnerThread != null) - { - //Remove all waiting mutex from the old owner, - //and insert then on the new owner. - UpdateMutexOwnerUnsafe(CurrThread, OwnerThread, MutexAddress); - - CurrThread.UpdatePriority(); - - int HasListeners = Count >= 2 ? MutexHasListenersMask : 0; - - Memory.WriteInt32ToSharedAddr(MutexAddress, HasListeners | OwnerThread.WaitHandle); - - OwnerThread.WaitHandle = 0; - OwnerThread.MutexAddress = 0; - OwnerThread.CondVarAddress = 0; - OwnerThread.MutexOwner = null; - - OwnerThread.UpdatePriority(); - - Process.Scheduler.WakeUp(OwnerThread); - - Device.Log.PrintDebug(LogClass.KernelSvc, "Gave mutex to thread id " + OwnerThread.ThreadId + "!"); - } - else - { - Memory.WriteInt32ToSharedAddr(MutexAddress, 0); - - Device.Log.PrintDebug(LogClass.KernelSvc, "No threads waiting mutex!"); - } - } - } - - private bool CondVarWait( - KThread WaitThread, - int WaitThreadHandle, - long MutexAddress, - long CondVarAddress, - ulong Timeout) - { - WaitThread.WaitHandle = WaitThreadHandle; - WaitThread.MutexAddress = MutexAddress; - WaitThread.CondVarAddress = CondVarAddress; - - lock (Process.ThreadSyncLock) - { - MutexUnlock(WaitThread, MutexAddress); - - WaitThread.CondVarSignaled = false; - - Process.ThreadArbiterList.Add(WaitThread); - } - - Device.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state..."); - - if (Timeout != ulong.MaxValue) - { - Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout)); - - lock (Process.ThreadSyncLock) - { - if (!WaitThread.CondVarSignaled || WaitThread.MutexOwner != null) - { - if (WaitThread.MutexOwner != null) - { - WaitThread.MutexOwner.MutexWaiters.Remove(WaitThread); - WaitThread.MutexOwner.UpdatePriority(); - - WaitThread.MutexOwner = null; - } - - Process.ThreadArbiterList.Remove(WaitThread); - - Device.Log.PrintDebug(LogClass.KernelSvc, "Timed out..."); - - return false; - } - } - } - else - { - Process.Scheduler.EnterWait(WaitThread); - } - - return true; - } - - private void CondVarSignal( - AThreadState ThreadState, - KThread CurrThread, - long CondVarAddress, - int Count) - { - lock (Process.ThreadSyncLock) - { - while (Count == -1 || Count-- > 0) - { - KThread WaitThread = PopCondVarThreadUnsafe(CondVarAddress); - - if (WaitThread == null) - { - Device.Log.PrintDebug(LogClass.KernelSvc, "No more threads to wake up!"); - - break; - } - - WaitThread.CondVarSignaled = true; - - long MutexAddress = WaitThread.MutexAddress; - - Memory.SetExclusive(ThreadState, MutexAddress); - - int MutexValue = Memory.ReadInt32(MutexAddress); - - while (MutexValue != 0) - { - if (Memory.TestExclusive(ThreadState, MutexAddress)) - { - //Wait until the lock is released. - InsertWaitingMutexThreadUnsafe(MutexValue & ~MutexHasListenersMask, WaitThread); - - Memory.WriteInt32(MutexAddress, MutexValue | MutexHasListenersMask); - - Memory.ClearExclusiveForStore(ThreadState); - - break; - } - - Memory.SetExclusive(ThreadState, MutexAddress); - - MutexValue = Memory.ReadInt32(MutexAddress); - } - - Device.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = 0x" + MutexValue.ToString("x8")); - - if (MutexValue == 0) - { - //Give the lock to this thread. - Memory.WriteInt32ToSharedAddr(MutexAddress, WaitThread.WaitHandle); - - WaitThread.WaitHandle = 0; - WaitThread.MutexAddress = 0; - WaitThread.CondVarAddress = 0; - - WaitThread.MutexOwner?.UpdatePriority(); - - WaitThread.MutexOwner = null; - - Process.Scheduler.WakeUp(WaitThread); - } - } - } - } - - private void UpdateMutexOwnerUnsafe(KThread CurrThread, KThread NewOwner, long MutexAddress) - { - //Go through all threads waiting for the mutex, - //and update the MutexOwner field to point to the new owner. - for (int Index = 0; Index < CurrThread.MutexWaiters.Count; Index++) - { - KThread Thread = CurrThread.MutexWaiters[Index]; - - if (Thread.MutexAddress == MutexAddress) - { - CurrThread.MutexWaiters.RemoveAt(Index--); - - InsertWaitingMutexThreadUnsafe(NewOwner, Thread); - } - } - } - - private void InsertWaitingMutexThreadUnsafe(int OwnerThreadHandle, KThread WaitThread) - { - KThread OwnerThread = Process.HandleTable.GetData(OwnerThreadHandle); - - if (OwnerThread == null) - { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{OwnerThreadHandle:x8}!"); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); return; } - InsertWaitingMutexThreadUnsafe(OwnerThread, WaitThread); - } - - private void InsertWaitingMutexThreadUnsafe(KThread OwnerThread, KThread WaitThread) - { - WaitThread.MutexOwner = OwnerThread; - - if (!OwnerThread.MutexWaiters.Contains(WaitThread)) + if (IsAddressNotWordAligned(Address)) { - OwnerThread.MutexWaiters.Add(WaitThread); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); - OwnerThread.UpdatePriority(); - } - } + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); - private (KThread, int) PopMutexThreadUnsafe(KThread OwnerThread, long MutexAddress) - { - int Count = 0; - - KThread WakeThread = null; - - foreach (KThread Thread in OwnerThread.MutexWaiters) - { - if (Thread.MutexAddress != MutexAddress) - { - continue; - } - - if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority) - { - WakeThread = Thread; - } - - Count++; + return; } - if (WakeThread != null) + long Result; + + switch (Type) { - OwnerThread.MutexWaiters.Remove(WakeThread); + case SignalType.Signal: + Result = System.AddressArbiter.Signal(Address, Count); + break; + + case SignalType.SignalAndIncrementIfEqual: + Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count); + break; + + case SignalType.SignalAndModifyIfEqual: + Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count); + break; + + default: + Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue); + break; } - return (WakeThread, Count); - } - - private KThread PopCondVarThreadUnsafe(long CondVarAddress) - { - KThread WakeThread = null; - - foreach (KThread Thread in Process.ThreadArbiterList) + if (Result != 0) { - if (Thread.CondVarAddress != CondVarAddress) - { - continue; - } - - if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority) - { - WakeThread = Thread; - } + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } - if (WakeThread != null) - { - Process.ThreadArbiterList.Remove(WakeThread); - } - - return WakeThread; + ThreadState.X0 = (ulong)Result; } private bool IsPointingInsideKernel(long Address) diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadQueue.cs b/Ryujinx.HLE/HOS/Kernel/ThreadQueue.cs deleted file mode 100644 index 815e86ad2a..0000000000 --- a/Ryujinx.HLE/HOS/Kernel/ThreadQueue.cs +++ /dev/null @@ -1,158 +0,0 @@ -namespace Ryujinx.HLE.HOS.Kernel -{ - class ThreadQueue - { - private const int LowestPriority = 0x3f; - - private SchedulerThread Head; - - private object ListLock; - - public ThreadQueue() - { - ListLock = new object(); - } - - public void Push(SchedulerThread Wait) - { - lock (ListLock) - { - //Ensure that we're not creating circular references - //by adding a thread that is already on the list. - if (HasThread(Wait)) - { - return; - } - - if (Head == null || Head.Thread.ActualPriority >= Wait.Thread.ActualPriority) - { - Wait.Next = Head; - - Head = Wait; - - return; - } - - SchedulerThread Curr = Head; - - while (Curr.Next != null) - { - if (Curr.Next.Thread.ActualPriority >= Wait.Thread.ActualPriority) - { - break; - } - - Curr = Curr.Next; - } - - Wait.Next = Curr.Next; - Curr.Next = Wait; - } - } - - public SchedulerThread Pop(int Core, int MinPriority = LowestPriority) - { - lock (ListLock) - { - int CoreMask = 1 << Core; - - SchedulerThread Prev = null; - SchedulerThread Curr = Head; - - while (Curr != null) - { - KThread Thread = Curr.Thread; - - if (Thread.ActualPriority <= MinPriority && (Thread.CoreMask & CoreMask) != 0) - { - if (Prev != null) - { - Prev.Next = Curr.Next; - } - else - { - Head = Head.Next; - } - - break; - } - - Prev = Curr; - Curr = Curr.Next; - } - - return Curr; - } - } - - public bool Remove(SchedulerThread Thread) - { - lock (ListLock) - { - if (Head == null) - { - return false; - } - else if (Head == Thread) - { - Head = Head.Next; - - return true; - } - - SchedulerThread Prev = Head; - SchedulerThread Curr = Head.Next; - - while (Curr != null) - { - if (Curr == Thread) - { - Prev.Next = Curr.Next; - - return true; - } - - Prev = Curr; - Curr = Curr.Next; - } - - return false; - } - } - - public bool Resort(SchedulerThread Thread) - { - lock (ListLock) - { - if (Remove(Thread)) - { - Push(Thread); - - return true; - } - - return false; - } - } - - public bool HasThread(SchedulerThread Thread) - { - lock (ListLock) - { - SchedulerThread Curr = Head; - - while (Curr != null) - { - if (Curr == Thread) - { - return true; - } - - Curr = Curr.Next; - } - - return false; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs new file mode 100644 index 0000000000..603446f3cf --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum ThreadSchedState : byte + { + LowNibbleMask = 0xf, + HighNibbleMask = 0xf0, + ExceptionalMask = 0x70, + ForcePauseFlag = 0x20, + + None = 0, + Paused = 1, + Running = 2, + TerminationPending = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs index bfda93f494..93b2d68d0c 100644 --- a/Ryujinx.HLE/HOS/Process.cs +++ b/Ryujinx.HLE/HOS/Process.cs @@ -2,15 +2,16 @@ using ChocolArm64; using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; +using LibHac; +using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; -using Ryujinx.HLE.HOS.Diagnostics; +using Ryujinx.HLE.HOS.Diagnostics.Demangler; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Nv; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System; using System.Collections.Concurrent; @@ -32,22 +33,18 @@ namespace Ryujinx.HLE.HOS public int ProcessId { get; private set; } - private ATranslator Translator; + private Translator Translator; - public AMemory Memory { get; private set; } + public MemoryManager Memory { get; private set; } public KMemoryManager MemoryManager { get; private set; } private List TlsPages; - public KProcessScheduler Scheduler { get; private set; } - - public List ThreadArbiterList { get; private set; } - - public object ThreadSyncLock { get; private set; } - public Npdm MetaData { get; private set; } + public Nacp ControlData { get; set; } + public KProcessHandleTable HandleTable { get; private set; } public AppletStateMgr AppletState { get; private set; } @@ -58,34 +55,42 @@ namespace Ryujinx.HLE.HOS private List Executables; - private Dictionary SymbolTable; - private long ImageBase; - private bool ShouldDispose; - private bool Disposed; - public Process(Switch Device, KProcessScheduler Scheduler, int ProcessId, Npdm MetaData) + public Process(Switch Device, int ProcessId, Npdm MetaData) { this.Device = Device; - this.Scheduler = Scheduler; this.MetaData = MetaData; this.ProcessId = ProcessId; - Memory = new AMemory(Device.Memory.RamPointer); + Memory = new MemoryManager(Device.Memory.RamPointer); + + Memory.InvalidAccess += CpuInvalidAccessHandler; MemoryManager = new KMemoryManager(this); TlsPages = new List(); - ThreadArbiterList = new List(); + int HandleTableSize = 1024; - ThreadSyncLock = new object(); + if (MetaData != null) + { + foreach (KernelAccessControlItem Item in MetaData.ACI0.KernelAccessControl.Items) + { + if (Item.HasHandleTableSize) + { + HandleTableSize = Item.HandleTableSize; - HandleTable = new KProcessHandleTable(); + break; + } + } + } - AppletState = new AppletStateMgr(); + HandleTable = new KProcessHandleTable(Device.System, HandleTableSize); + + AppletState = new AppletStateMgr(Device.System); SvcHandler = new SvcHandler(Device, this); @@ -103,13 +108,37 @@ namespace Ryujinx.HLE.HOS throw new ObjectDisposedException(nameof(Process)); } - Device.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}."); + long ImageEnd = LoadProgram(Program, ImageBase); - Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase); + ImageBase = IntUtils.AlignUp(ImageEnd, KMemoryManager.PageSize); + } + + public long LoadProgram(IExecutable Program, long ExecutableBase) + { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + Logger.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}."); + + Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase); Executables.Add(Executable); - ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize); + return Executable.ImageEnd; + } + + public void RemoveProgram(long ExecutableBase) + { + foreach (Executable Executable in Executables) + { + if (Executable.ImageBase == ExecutableBase) + { + Executables.Remove(Executable); + break; + } + } } public void SetEmptyArgs() @@ -132,8 +161,6 @@ namespace Ryujinx.HLE.HOS return false; } - MakeSymbolTable(); - long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize; long MainStackSize = 1 * 1024 * 1024; @@ -153,7 +180,7 @@ namespace Ryujinx.HLE.HOS return false; } - KThread MainThread = HandleTable.GetData(Handle); + KThread MainThread = HandleTable.GetKThread(Handle); if (NeedsHbAbi) { @@ -171,15 +198,17 @@ namespace Ryujinx.HLE.HOS Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath); - MainThread.Thread.ThreadState.X0 = (ulong)HbAbiDataPosition; - MainThread.Thread.ThreadState.X1 = ulong.MaxValue; + MainThread.Context.ThreadState.X0 = (ulong)HbAbiDataPosition; + MainThread.Context.ThreadState.X1 = ulong.MaxValue; } - Scheduler.StartThread(MainThread); + MainThread.TimeUp(); return true; } + private int ThreadIdCtr = 1; + public int MakeThread( long EntryPoint, long StackTop, @@ -192,17 +221,17 @@ namespace Ryujinx.HLE.HOS throw new ObjectDisposedException(nameof(Process)); } - AThread CpuThread = new AThread(GetTranslator(), Memory, EntryPoint); + CpuThread CpuThread = new CpuThread(GetTranslator(), Memory, EntryPoint); long Tpidr = GetFreeTls(); - int ThreadId = (int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1; + int ThreadId = ThreadIdCtr++; //(int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1; - KThread Thread = new KThread(CpuThread, this, ProcessorId, Priority, ThreadId); + KThread Thread = new KThread(CpuThread, this, Device.System, ProcessorId, Priority, ThreadId); Thread.LastPc = EntryPoint; - int Handle = HandleTable.OpenHandle(Thread); + HandleTable.GenerateHandle(Thread, out int Handle); CpuThread.ThreadState.CntfrqEl0 = TickFreq; CpuThread.ThreadState.Tpidr = Tpidr; @@ -211,6 +240,7 @@ namespace Ryujinx.HLE.HOS CpuThread.ThreadState.X1 = (ulong)Handle; CpuThread.ThreadState.X31 = (ulong)StackTop; + CpuThread.ThreadState.Interrupt += InterruptHandler; CpuThread.ThreadState.Break += BreakHandler; CpuThread.ThreadState.SvcCall += SvcHandler.SvcCall; CpuThread.ThreadState.Undefined += UndefinedHandler; @@ -248,41 +278,25 @@ namespace Ryujinx.HLE.HOS return Position; } - private void BreakHandler(object sender, AInstExceptionEventArgs e) + private void InterruptHandler(object sender, EventArgs e) { + Device.System.Scheduler.ContextSwitch(); + } + + private void BreakHandler(object sender, InstExceptionEventArgs e) + { + PrintStackTraceForCurrentThread(); + throw new GuestBrokeExecutionException(); } - private void UndefinedHandler(object sender, AInstUndefinedEventArgs e) + private void UndefinedHandler(object sender, InstUndefinedEventArgs e) { + PrintStackTraceForCurrentThread(); + throw new UndefinedInstructionException(e.Position, e.RawOpCode); } - private void MakeSymbolTable() - { - SymbolTable = new Dictionary(); - - foreach (Executable Exe in Executables) - { - foreach (KeyValuePair KV in Exe.SymbolTable) - { - SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value); - } - } - } - - private ATranslator GetTranslator() - { - if (Translator == null) - { - Translator = new ATranslator(SymbolTable); - - Translator.CpuTrace += CpuTraceHandler; - } - - return Translator; - } - public void EnableCpuTracing() { Translator.EnableCpuTrace = true; @@ -293,34 +307,73 @@ namespace Ryujinx.HLE.HOS Translator.EnableCpuTrace = false; } - private void CpuTraceHandler(object sender, ACpuTraceEventArgs e) + private void CpuTraceHandler(object sender, CpuTraceEventArgs e) { - string NsoName = string.Empty; + Executable Exe = GetExecutable(e.Position); - for (int Index = Executables.Count - 1; Index >= 0; Index--) + if (Exe == null) { - if (e.Position >= Executables[Index].ImageBase) + return; + } + + if (!TryGetSubName(Exe, e.Position, out string SubName)) + { + SubName = string.Empty; + } + + long Offset = e.Position - Exe.ImageBase; + + string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}"; + + Logger.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName); + } + + private Translator GetTranslator() + { + if (Translator == null) + { + Translator = new Translator(); + + Translator.CpuTrace += CpuTraceHandler; + } + + return Translator; + } + + private void CpuInvalidAccessHandler(object sender, InvalidAccessEventArgs e) + { + PrintStackTraceForCurrentThread(); + } + + private void PrintStackTraceForCurrentThread() + { + foreach (KThread Thread in Threads.Values) + { + if (Thread.Context.IsCurrentThread()) { - NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}"; + PrintStackTrace(Thread.Context.ThreadState); break; } } - - Device.Log.PrintDebug(LogClass.Cpu, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}"); } - public void PrintStackTrace(AThreadState ThreadState) + public void PrintStackTrace(CpuThreadState ThreadState) { - long[] Positions = ThreadState.GetCallStack(); - StringBuilder Trace = new StringBuilder(); Trace.AppendLine("Guest stack trace:"); - foreach (long Position in Positions) + void AppendTrace(long Position) { - if (!SymbolTable.TryGetValue(Position, out string SubName)) + Executable Exe = GetExecutable(Position); + + if (Exe == null) + { + return; + } + + if (!TryGetSubName(Exe, Position, out string SubName)) { SubName = $"Sub{Position:x16}"; } @@ -329,40 +382,87 @@ namespace Ryujinx.HLE.HOS SubName = Demangler.Parse(SubName); } - Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")"); + long Offset = Position - Exe.ImageBase; + + string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}"; + + Trace.AppendLine(" " + ExeNameWithAddr + " " + SubName); } - Device.Log.PrintInfo(LogClass.Cpu, Trace.ToString()); + long FramePointer = (long)ThreadState.X29; + + while (FramePointer != 0) + { + AppendTrace(Memory.ReadInt64(FramePointer + 8)); + + FramePointer = Memory.ReadInt64(FramePointer); + } + + Logger.PrintInfo(LogClass.Cpu, Trace.ToString()); } - private string GetNsoNameAndAddress(long Position) + private bool TryGetSubName(Executable Exe, long Position, out string Name) + { + Position -= Exe.ImageBase; + + int Left = 0; + int Right = Exe.SymbolTable.Count - 1; + + while (Left <= Right) + { + int Size = Right - Left; + + int Middle = Left + (Size >> 1); + + ElfSym Symbol = Exe.SymbolTable[Middle]; + + long EndPosition = Symbol.Value + Symbol.Size; + + if ((ulong)Position >= (ulong)Symbol.Value && (ulong)Position < (ulong)EndPosition) + { + Name = Symbol.Name; + + return true; + } + + if ((ulong)Position < (ulong)Symbol.Value) + { + Right = Middle - 1; + } + else + { + Left = Middle + 1; + } + } + + Name = null; + + return false; + } + + private Executable GetExecutable(long Position) { string Name = string.Empty; for (int Index = Executables.Count - 1; Index >= 0; Index--) { - if (Position >= Executables[Index].ImageBase) + if ((ulong)Position >= (ulong)Executables[Index].ImageBase) { - long Offset = Position - Executables[Index].ImageBase; - - Name = $"{Executables[Index].Name}:{Offset:x8}"; - - break; + return Executables[Index]; } } - return Name; + return null; } private void ThreadFinished(object sender, EventArgs e) { - if (sender is AThread Thread) + if (sender is CpuThread Thread) { - Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread); - - Scheduler.RemoveThread(KernelThread); - - KernelThread.WaitEvent.Set(); + if (Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread)) + { + Device.System.Scheduler.RemoveThread(KernelThread); + } } if (Threads.Count == 0) @@ -390,24 +490,16 @@ namespace Ryujinx.HLE.HOS Disposed = true; - foreach (object Obj in HandleTable.Clear()) - { - if (Obj is KSession Session) - { - Session.Dispose(); - } - } + HandleTable.Destroy(); INvDrvServices.UnloadProcess(this); - AppletState.Dispose(); - if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix)) { File.Delete(Executables[0].FilePath); } - Device.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); + Logger.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); } public void Dispose() @@ -423,9 +515,7 @@ namespace Ryujinx.HLE.HOS { foreach (KThread Thread in Threads.Values) { - Thread.Thread.StopExecution(); - - Scheduler.ForceWakeUp(Thread); + Device.System.Scheduler.StopThread(Thread); } } else diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/Ryujinx.HLE/HOS/ServiceCtx.cs index d8c9fdf6ed..a591673e2c 100644 --- a/Ryujinx.HLE/HOS/ServiceCtx.cs +++ b/Ryujinx.HLE/HOS/ServiceCtx.cs @@ -7,24 +7,24 @@ namespace Ryujinx.HLE.HOS { class ServiceCtx { - public Switch Device { get; private set; } - public Process Process { get; private set; } - public AMemory Memory { get; private set; } - public KSession Session { get; private set; } - public IpcMessage Request { get; private set; } - public IpcMessage Response { get; private set; } - public BinaryReader RequestData { get; private set; } - public BinaryWriter ResponseData { get; private set; } + public Switch Device { get; private set; } + public Process Process { get; private set; } + public MemoryManager Memory { get; private set; } + public KSession Session { get; private set; } + public IpcMessage Request { get; private set; } + public IpcMessage Response { get; private set; } + public BinaryReader RequestData { get; private set; } + public BinaryWriter ResponseData { get; private set; } public ServiceCtx( - Switch Device, - Process Process, - AMemory Memory, - KSession Session, - IpcMessage Request, - IpcMessage Response, - BinaryReader RequestData, - BinaryWriter ResponseData) + Switch Device, + Process Process, + MemoryManager Memory, + KSession Session, + IpcMessage Request, + IpcMessage Response, + BinaryReader RequestData, + BinaryWriter ResponseData) { this.Device = Device; this.Process = Process; diff --git a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs index 8fd7bfeafd..f920c00ba0 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs @@ -1,6 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Logging; +using Ryujinx.HLE.Utilities; using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -23,11 +24,14 @@ namespace Ryujinx.HLE.HOS.Services.Acc { 3, ListOpenUsers }, { 4, GetLastOpenedUser }, { 5, GetProfile }, + { 50, IsUserRegistrationRequestPermitted }, + { 51, TrySelectUserWithoutInteraction }, { 100, InitializeApplicationInfo }, { 101, GetBaasAccountManagerForApplication } }; } + // GetUserCount() -> i32 public long GetUserCount(ServiceCtx Context) { Context.ResponseData.Write(Context.Device.System.State.GetUserCount()); @@ -35,22 +39,25 @@ namespace Ryujinx.HLE.HOS.Services.Acc return 0; } + // GetUserExistence(nn::account::Uid) -> bool public long GetUserExistence(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); - Context.ResponseData.Write(Context.Device.System.State.TryGetUser(Uuid, out _) ? 1 : 0); + Context.ResponseData.Write(Context.Device.System.State.TryGetUser(Uuid, out _)); return 0; } + // ListAllUsers() -> array public long ListAllUsers(ServiceCtx Context) { return WriteUserList(Context, Context.Device.System.State.GetAllUsers()); } + // ListOpenUsers() -> array public long ListOpenUsers(ServiceCtx Context) { return WriteUserList(Context, Context.Device.System.State.GetOpenUsers()); @@ -70,17 +77,14 @@ namespace Ryujinx.HLE.HOS.Services.Acc break; } - byte[] Uuid = Profile.Uuid.Bytes; - - for (int Index = Uuid.Length - 1; Index >= 0; Index--) - { - Context.Memory.WriteByte(OutputPosition + Offset++, Uuid[Index]); - } + Context.Memory.WriteInt64(OutputPosition, Profile.Uuid.Low); + Context.Memory.WriteInt64(OutputPosition + 8, Profile.Uuid.High); } return 0; } + // GetLastOpenedUser() -> nn::account::Uid public long GetLastOpenedUser(ServiceCtx Context) { UserProfile LastOpened = Context.Device.System.State.LastOpenUser; @@ -90,15 +94,16 @@ namespace Ryujinx.HLE.HOS.Services.Acc return 0; } + // GetProfile(nn::account::Uid) -> object public long GetProfile(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); if (!Context.Device.System.State.TryGetUser(Uuid, out UserProfile Profile)) { - Context.Device.Log.PrintWarning(LogClass.ServiceAcc, $"User 0x{Uuid} not found!"); + Logger.PrintWarning(LogClass.ServiceAcc, $"User 0x{Uuid} not found!"); return MakeError(ErrorModule.Account, AccErr.UserNotFound); } @@ -108,16 +113,50 @@ namespace Ryujinx.HLE.HOS.Services.Acc return 0; } - public long InitializeApplicationInfo(ServiceCtx Context) + // IsUserRegistrationRequestPermitted(u64, pid) -> bool + public long IsUserRegistrationRequestPermitted(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + long Unknown = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + + Context.ResponseData.Write(false); return 0; } + // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid + public long TrySelectUserWithoutInteraction(ServiceCtx Context) + { + bool Unknown = Context.RequestData.ReadBoolean(); + + Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + + UserProfile Profile = Context.Device.System.State.LastOpenUser; + + Profile.Uuid.Write(Context.ResponseData); + + return 0; + } + + // InitializeApplicationInfo(u64, pid) + public long InitializeApplicationInfo(ServiceCtx Context) + { + long Unknown = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + + return 0; + } + + // GetBaasAccountManagerForApplication(nn::account::Uid) -> object public long GetBaasAccountManagerForApplication(ServiceCtx Context) { - MakeObject(Context, new IManagerForApplication()); + UInt128 Uuid = new UInt128( + Context.RequestData.ReadInt64(), + Context.RequestData.ReadInt64()); + + MakeObject(Context, new IManagerForApplication(Uuid)); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs b/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs index 813a1b17e5..9312b2bc82 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs @@ -1,36 +1,45 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; +using Ryujinx.HLE.Utilities; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Acc { class IManagerForApplication : IpcService { + private UInt128 Uuid; + private Dictionary m_Commands; public override IReadOnlyDictionary Commands => m_Commands; - public IManagerForApplication() + public IManagerForApplication(UInt128 Uuid) { m_Commands = new Dictionary() { { 0, CheckAvailability }, { 1, GetAccountId } }; + + this.Uuid = Uuid; } + // CheckAvailability() public long CheckAvailability(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAcc, "Stubbed."); return 0; } + // GetAccountId() -> nn::account::NetworkServiceAccountId public long GetAccountId(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + long NetworkServiceAccountId = 0xcafe; - Context.ResponseData.Write(0xcafeL); + Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. NetworkServiceAccountId: {NetworkServiceAccountId}"); + + Context.ResponseData.Write(NetworkServiceAccountId); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs b/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs index 316f16d082..1d1a15cbfb 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs @@ -1,9 +1,8 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; -using System; using System.Collections.Generic; using System.IO; using System.Reflection; @@ -38,11 +37,11 @@ namespace Ryujinx.HLE.HOS.Services.Acc public long Get(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAcc, "Stubbed."); long Position = Context.Request.ReceiveBuff[0].Position; - AMemoryHelper.FillWithZeros(Context.Memory, Position, 0x80); + MemoryHelper.FillWithZeros(Context.Memory, Position, 0x80); Context.Memory.WriteInt32(Position, 0); Context.Memory.WriteInt32(Position + 4, 1); diff --git a/Ryujinx.HLE/HOS/Services/Am/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/IApplicationFunctions.cs index 0a10d2a66c..1934798b2d 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IApplicationFunctions.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Am long UIdLow = Context.RequestData.ReadInt64(); long UIdHigh = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); Context.ResponseData.Write(0L); @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Am string Result = GetFormattedErrorCode(ErrorCode); - Context.Device.Log.PrintInfo(LogClass.ServiceAm, $"Result = 0x{ErrorCode:x8} ({Result})."); + Logger.PrintInfo(LogClass.ServiceAm, $"Result = 0x{ErrorCode:x8} ({Result})."); return 0; } @@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetPseudoDeviceId(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); Context.ResponseData.Write(0L); Context.ResponseData.Write(0L); @@ -100,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long InitializeGamePlayRecording(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { int State = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/IApplicationProxy.cs b/Ryujinx.HLE/HOS/Services/Am/IApplicationProxy.cs index 4003f1515a..2aaeda7826 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IApplicationProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IApplicationProxy.cs @@ -26,14 +26,14 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetCommonStateGetter(ServiceCtx Context) { - MakeObject(Context, new ICommonStateGetter()); + MakeObject(Context, new ICommonStateGetter(Context.Device.System)); return 0; } public long GetSelfController(ServiceCtx Context) { - MakeObject(Context, new ISelfController()); + MakeObject(Context, new ISelfController(Context.Device.System)); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/IAudioController.cs b/Ryujinx.HLE/HOS/Services/Am/IAudioController.cs index 8968ad72c3..062f2d8651 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IAudioController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IAudioController.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Am float AppletVolume = Context.RequestData.ReadSingle(); float LibraryAppletVolume = Context.RequestData.ReadSingle(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { Context.ResponseData.Write(1f); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { Context.ResponseData.Write(1f); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Am float Unknown0 = Context.RequestData.ReadSingle(); long Unknown1 = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { float Unknown0 = Context.RequestData.ReadSingle(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs index 3cdfbbdb95..6b012689c4 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs @@ -1,6 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Am private KEvent DisplayResolutionChangeEvent; - public ICommonStateGetter() + public ICommonStateGetter(Horizon System) { m_Commands = new Dictionary() { @@ -29,14 +30,17 @@ namespace Ryujinx.HLE.HOS.Services.Am { 61, GetDefaultDisplayResolutionChangeEvent } }; - DisplayResolutionChangeEvent = new KEvent(); + DisplayResolutionChangeEvent = new KEvent(System); } public long GetEventHandle(ServiceCtx Context) { KEvent Event = Context.Process.AppletState.MessageEvent; - int Handle = Context.Process.HandleTable.OpenHandle(Event); + if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -81,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { Context.ResponseData.Write((byte)0); //Unknown value. - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -103,11 +107,14 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetDefaultDisplayResolutionChangeEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(DisplayResolutionChangeEvent); + if (Context.Process.HandleTable.GenerateHandle(DisplayResolutionChangeEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs index 95028ca047..3f026e2fe6 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs @@ -1,6 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -13,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Am private KEvent ChannelEvent; - public IHomeMenuFunctions() + public IHomeMenuFunctions(Horizon System) { m_Commands = new Dictionary() { @@ -22,23 +23,26 @@ namespace Ryujinx.HLE.HOS.Services.Am }; //ToDo: Signal this Event somewhere in future. - ChannelEvent = new KEvent(); + ChannelEvent = new KEvent(System); } public long RequestToGetForeground(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long GetPopFromGeneralChannelEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(ChannelEvent); + if (Context.Process.HandleTable.GenerateHandle(ChannelEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs index e099ec64a7..9e0d0e7075 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs @@ -1,6 +1,7 @@ -using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -13,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Am private KEvent StateChangedEvent; - public ILibraryAppletAccessor() + public ILibraryAppletAccessor(Horizon System) { m_Commands = new Dictionary() { @@ -24,39 +25,42 @@ namespace Ryujinx.HLE.HOS.Services.Am { 101, PopOutData } }; - StateChangedEvent = new KEvent(); + StateChangedEvent = new KEvent(System); } public long GetAppletStateChangedEvent(ServiceCtx Context) { - StateChangedEvent.WaitEvent.Set(); + StateChangedEvent.ReadableEvent.Signal(); - int Handle = Context.Process.HandleTable.OpenHandle(StateChangedEvent); + if (Context.Process.HandleTable.GenerateHandle(StateChangedEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long Start(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long GetResult(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long PushInData(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletCreator.cs b/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletCreator.cs index 065574c784..5535a43c7c 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletCreator.cs @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long CreateLibraryApplet(ServiceCtx Context) { - MakeObject(Context, new ILibraryAppletAccessor()); + MakeObject(Context, new ILibraryAppletAccessor(Context.Device.System)); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs index ccd96e0d2d..2abaee2e7b 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs @@ -1,6 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -13,7 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Am private KEvent LaunchableEvent; - public ISelfController() + private int IdleTimeDetectionExtension; + + public ISelfController(Horizon System) { m_Commands = new Dictionary() { @@ -28,42 +31,47 @@ namespace Ryujinx.HLE.HOS.Services.Am { 14, SetRestartMessageEnabled }, { 16, SetOutOfFocusSuspendingEnabled }, { 19, SetScreenShotImageOrientation }, - { 50, SetHandlesRequestToDisplay } + { 50, SetHandlesRequestToDisplay }, + { 62, SetIdleTimeDetectionExtension }, + { 63, GetIdleTimeDetectionExtension } }; - LaunchableEvent = new KEvent(); + LaunchableEvent = new KEvent(System); } public long Exit(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long LockExit(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long UnlockExit(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long GetLibraryAppletLaunchableEvent(ServiceCtx Context) { - LaunchableEvent.WaitEvent.Set(); + LaunchableEvent.ReadableEvent.Signal(); - int Handle = Context.Process.HandleTable.OpenHandle(LaunchableEvent); + if (Context.Process.HandleTable.GenerateHandle(LaunchableEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -72,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -81,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -90,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -101,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Am bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false; bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -110,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -119,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -128,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { int Orientation = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -137,7 +145,27 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + + // SetIdleTimeDetectionExtension(u32) + public long SetIdleTimeDetectionExtension(ServiceCtx Context) + { + IdleTimeDetectionExtension = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}"); + + return 0; + } + + // GetIdleTimeDetectionExtension() -> u32 + public long GetIdleTimeDetectionExtension(ServiceCtx Context) + { + Context.ResponseData.Write(IdleTimeDetectionExtension); + + Logger.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ISystemAppletProxy.cs b/Ryujinx.HLE/HOS/Services/Am/ISystemAppletProxy.cs index c08d401802..85e11e0fd0 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ISystemAppletProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ISystemAppletProxy.cs @@ -28,14 +28,14 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetCommonStateGetter(ServiceCtx Context) { - MakeObject(Context, new ICommonStateGetter()); + MakeObject(Context, new ICommonStateGetter(Context.Device.System)); return 0; } public long GetSelfController(ServiceCtx Context) { - MakeObject(Context, new ISelfController()); + MakeObject(Context, new ISelfController(Context.Device.System)); return 0; } @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetHomeMenuFunctions(ServiceCtx Context) { - MakeObject(Context, new IHomeMenuFunctions()); + MakeObject(Context, new IHomeMenuFunctions(Context.Device.System)); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/IWindowController.cs b/Ryujinx.HLE/HOS/Services/Am/IWindowController.cs index 1a5a716f9a..de5137d120 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IWindowController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IWindowController.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetAppletResourceUserId(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); Context.ResponseData.Write(0L); @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long AcquireForegroundRights(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Apm/ISession.cs b/Ryujinx.HLE/HOS/Services/Apm/ISession.cs index 739e264d58..d04bcfc973 100644 --- a/Ryujinx.HLE/HOS/Services/Apm/ISession.cs +++ b/Ryujinx.HLE/HOS/Services/Apm/ISession.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Apm @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm Context.ResponseData.Write((uint)PerformanceConfiguration.PerformanceConfiguration1); - Context.Device.Log.PrintStub(LogClass.ServiceApm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceApm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudErr.cs b/Ryujinx.HLE/HOS/Services/Aud/AudErr.cs index cecea86057..675ea8c7c9 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudErr.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudErr.cs @@ -5,5 +5,6 @@ namespace Ryujinx.HLE.HOS.Services.Aud public const int DeviceNotFound = 1; public const int UnsupportedRevision = 2; public const int UnsupportedSampleRate = 3; + public const int OpusInvalidInput = 6; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioOut/IAudioOut.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioOut/IAudioOut.cs index 81561f046f..1ad049c6bd 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioOut/IAudioOut.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioOut/IAudioOut.cs @@ -67,7 +67,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioOut public long RegisterBufferEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(ReleaseEvent); + if (Context.Process.HandleTable.GenerateHandle(ReleaseEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -102,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioOut { long Tag = Context.RequestData.ReadInt64(); - AudioOutData Data = AMemoryHelper.Read( + AudioOutData Data = MemoryHelper.Read( Context.Memory, Position); @@ -155,8 +158,6 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioOut if (Disposing) { AudioOut.CloseTrack(Track); - - ReleaseEvent.Dispose(); } } } diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs index 8c83338d1d..50a87893b5 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs @@ -1,13 +1,15 @@ using ChocolArm64.Memory; using Ryujinx.Audio; using Ryujinx.Audio.Adpcm; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer { @@ -26,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer private KEvent UpdateEvent; - private AMemory Memory; + private MemoryManager Memory; private IAalOutput AudioOut; @@ -38,17 +40,27 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer private int Track; - public IAudioRenderer(AMemory Memory, IAalOutput AudioOut, AudioRendererParameter Params) + private PlayState PlayState; + + public IAudioRenderer( + Horizon System, + MemoryManager Memory, + IAalOutput AudioOut, + AudioRendererParameter Params) { m_Commands = new Dictionary() { + { 0, GetSampleRate }, + { 1, GetSampleCount }, + { 2, GetMixBufferCount }, + { 3, GetState }, { 4, RequestUpdateAudioRenderer }, { 5, StartAudioRenderer }, { 6, StopAudioRenderer }, { 7, QuerySystemEvent } }; - UpdateEvent = new KEvent(); + UpdateEvent = new KEvent(System); this.Memory = Memory; this.AudioOut = AudioOut; @@ -64,11 +76,47 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer Voices = CreateArray(Params.VoiceCount); InitializeAudioOut(); + + PlayState = PlayState.Stopped; + } + + // GetSampleRate() -> u32 + public long GetSampleRate(ServiceCtx Context) + { + Context.ResponseData.Write(Params.SampleRate); + + return 0; + } + + // GetSampleCount() -> u32 + public long GetSampleCount(ServiceCtx Context) + { + Context.ResponseData.Write(Params.SampleCount); + + return 0; + } + + // GetMixBufferCount() -> u32 + public long GetMixBufferCount(ServiceCtx Context) + { + Context.ResponseData.Write(Params.MixCount); + + return 0; + } + + // GetState() -> u32 + private long GetState(ServiceCtx Context) + { + Context.ResponseData.Write((int)PlayState); + + Logger.PrintStub(LogClass.ServiceAudio, $"Stubbed. Renderer State: {Enum.GetName(typeof(PlayState), PlayState)}"); + + return 0; } private void AudioCallback() { - UpdateEvent.WaitEvent.Set(); + UpdateEvent.ReadableEvent.Signal(); } private static T[] CreateArray(int Size) where T : new() @@ -97,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer long OutputPosition = Context.Request.ReceiveBuff[0].Position; long OutputSize = Context.Request.ReceiveBuff[0].Size; - AMemoryHelper.FillWithZeros(Context.Memory, OutputPosition, (int)OutputSize); + MemoryHelper.FillWithZeros(Context.Memory, OutputPosition, (int)OutputSize); long InputPosition = Context.Request.SendBuff[0].Position; @@ -200,21 +248,28 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer public long StartAudioRenderer(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); + + PlayState = PlayState.Playing; return 0; } public long StopAudioRenderer(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); + + PlayState = PlayState.Stopped; return 0; } public long QuerySystemEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent); + if (Context.Process.HandleTable.GenerateHandle(UpdateEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -250,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer } } - private void AppendMixedBuffer(long Tag) + private unsafe void AppendMixedBuffer(long Tag) { int[] MixBuffer = new int[MixBufferSamplesCount * AudioConsts.HostChannelsCount]; @@ -261,9 +316,9 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer continue; } - int OutOffset = 0; - - int PendingSamples = MixBufferSamplesCount; + int OutOffset = 0; + int PendingSamples = MixBufferSamplesCount; + float Volume = Voice.Volume; while (PendingSamples > 0) { @@ -278,9 +333,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer for (int Offset = 0; Offset < Samples.Length; Offset++) { - int Sample = (int)(Samples[Offset] * Voice.Volume); - - MixBuffer[OutOffset++] += Sample; + MixBuffer[OutOffset++] += (int)(Samples[Offset] * Voice.Volume); } } } @@ -288,11 +341,49 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer AudioOut.AppendBuffer(Track, Tag, GetFinalBuffer(MixBuffer)); } - private static short[] GetFinalBuffer(int[] Buffer) + private unsafe static short[] GetFinalBuffer(int[] Buffer) { short[] Output = new short[Buffer.Length]; - for (int Offset = 0; Offset < Buffer.Length; Offset++) + int Offset = 0; + + // Perform Saturation using SSE2 if supported + if (Sse2.IsSupported) + { + fixed (int* inptr = Buffer) + fixed (short* outptr = Output) + { + for (; Offset + 32 <= Buffer.Length; Offset += 32) + { + // Unroll the loop a little to ensure the CPU pipeline + // is always full. + Vector128 block1A = Sse2.LoadVector128(inptr + Offset + 0); + Vector128 block1B = Sse2.LoadVector128(inptr + Offset + 4); + + Vector128 block2A = Sse2.LoadVector128(inptr + Offset + 8); + Vector128 block2B = Sse2.LoadVector128(inptr + Offset + 12); + + Vector128 block3A = Sse2.LoadVector128(inptr + Offset + 16); + Vector128 block3B = Sse2.LoadVector128(inptr + Offset + 20); + + Vector128 block4A = Sse2.LoadVector128(inptr + Offset + 24); + Vector128 block4B = Sse2.LoadVector128(inptr + Offset + 28); + + Vector128 output1 = Sse2.PackSignedSaturate(block1A, block1B); + Vector128 output2 = Sse2.PackSignedSaturate(block2A, block2B); + Vector128 output3 = Sse2.PackSignedSaturate(block3A, block3B); + Vector128 output4 = Sse2.PackSignedSaturate(block4A, block4B); + + Sse2.Store(outptr + Offset + 0, output1); + Sse2.Store(outptr + Offset + 8, output2); + Sse2.Store(outptr + Offset + 16, output3); + Sse2.Store(outptr + Offset + 24, output4); + } + } + } + + // Process left overs + for (; Offset < Buffer.Length; Offset++) { Output[Offset] = DspUtils.Saturate(Buffer[Offset]); } @@ -310,8 +401,6 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer if (Disposing) { AudioOut.CloseTrack(Track); - - UpdateEvent.Dispose(); } } } diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs index bee574ff85..7d6e1c583e 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs @@ -61,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer OutStatus.VoiceDropsCount = 0; } - public int[] GetBufferData(AMemory Memory, int MaxSamples, out int SamplesCount) + public int[] GetBufferData(MemoryManager Memory, int MaxSamples, out int SamplesCount) { if (!Playing) { @@ -118,13 +118,20 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer return Output; } - private void UpdateBuffer(AMemory Memory) + private void UpdateBuffer(MemoryManager Memory) { //TODO: Implement conversion for formats other //than interleaved stereo (2 channels). //As of now, it assumes that HostChannelsCount == 2. WaveBuffer Wb = WaveBuffers[BufferIndex]; + if (Wb.Position == 0) + { + Samples = new int[0]; + + return; + } + if (SampleFormat == SampleFormat.PcmInt16) { int SamplesCount = (int)(Wb.Size / (sizeof(short) * ChannelsCount)); diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs index 2e6056efb4..f9c0d315a3 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs @@ -1,7 +1,8 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; using System.Text; @@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud private KEvent SystemEvent; - public IAudioDevice() + public IAudioDevice(Horizon System) { m_Commands = new Dictionary() { @@ -32,10 +33,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud { 12, QueryAudioDeviceOutputEvent } }; - SystemEvent = new KEvent(); + SystemEvent = new KEvent(System); //TODO: We shouldn't be signaling this here. - SystemEvent.WaitEvent.Set(); + SystemEvent.ReadableEvent.Signal(); } public long ListAudioDeviceName(ServiceCtx Context) @@ -55,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud if ((Position - BasePosition) + Buffer.Length > Size) { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); break; } @@ -79,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud string DeviceName = Encoding.ASCII.GetString(DeviceNameBuffer); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -99,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud } else { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); } return 0; @@ -107,11 +108,14 @@ namespace Ryujinx.HLE.HOS.Services.Aud public long QueryAudioDeviceSystemEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); + if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -120,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud { Context.ResponseData.Write(2); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -141,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud if ((Position - BasePosition) + Buffer.Length > Size) { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); break; } @@ -164,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud string DeviceName = Encoding.UTF8.GetString(DeviceNameBuffer); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -173,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud { Context.ResponseData.Write(1f); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -192,7 +196,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud } else { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); } return 0; @@ -200,22 +204,28 @@ namespace Ryujinx.HLE.HOS.Services.Aud public long QueryAudioDeviceInputEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); + if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } public long QueryAudioDeviceOutputEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); + if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs index 8d2435b083..b08f7640f4 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs @@ -1,9 +1,9 @@ using ChocolArm64.Memory; using Ryujinx.Audio; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Aud.AudioOut; -using Ryujinx.HLE.Logging; using System.Collections.Generic; using System.Text; @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud } else { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); } Context.ResponseData.Write(NameCount); @@ -96,7 +96,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud private long OpenAudioOutImpl(ServiceCtx Context, long SendPosition, long SendSize, long ReceivePosition, long ReceiveSize) { - string DeviceName = AMemoryHelper.ReadAsciiString( + string DeviceName = MemoryHelper.ReadAsciiString( Context.Memory, SendPosition, SendSize); @@ -108,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud if (DeviceName != DefaultAudioOutput) { - Context.Device.Log.PrintWarning(LogClass.Audio, "Invalid device name!"); + Logger.PrintWarning(LogClass.Audio, "Invalid device name!"); return MakeError(ErrorModule.Audio, AudErr.DeviceNotFound); } @@ -121,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud } else { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {ReceiveSize} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {ReceiveSize} too small!"); } int SampleRate = Context.RequestData.ReadInt32(); @@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud if (SampleRate != DefaultSampleRate) { - Context.Device.Log.PrintWarning(LogClass.Audio, "Invalid sample rate!"); + Logger.PrintWarning(LogClass.Audio, "Invalid sample rate!"); return MakeError(ErrorModule.Audio, AudErr.UnsupportedSampleRate); } @@ -146,11 +146,11 @@ namespace Ryujinx.HLE.HOS.Services.Aud Channels = DefaultChannelsCount; } - KEvent ReleaseEvent = new KEvent(); + KEvent ReleaseEvent = new KEvent(Context.Device.System); ReleaseCallback Callback = () => { - ReleaseEvent.WaitEvent.Set(); + ReleaseEvent.ReadableEvent.Signal(); }; IAalOutput AudioOut = Context.Device.AudioOut; diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs index faa422901b..48a449ccc9 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs @@ -1,7 +1,7 @@ using Ryujinx.Audio; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services.Aud.AudioRenderer; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System.Collections.Generic; @@ -28,9 +28,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud { m_Commands = new Dictionary() { - { 0, OpenAudioRenderer }, - { 1, GetAudioRendererWorkBufferSize }, - { 2, GetAudioDevice } + { 0, OpenAudioRenderer }, + { 1, GetAudioRendererWorkBufferSize }, + { 2, GetAudioDeviceService }, + { 4, GetAudioDeviceServiceWithRevisionInfo } }; } @@ -40,7 +41,11 @@ namespace Ryujinx.HLE.HOS.Services.Aud AudioRendererParameter Params = GetAudioRendererParameter(Context); - MakeObject(Context, new IAudioRenderer(Context.Memory, AudioOut, Params)); + MakeObject(Context, new IAudioRenderer( + Context.Device.System, + Context.Memory, + AudioOut, + Params)); return 0; } @@ -98,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud Context.ResponseData.Write(Size); - Context.Device.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}."); + Logger.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}."); return 0; } @@ -106,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud { Context.ResponseData.Write(0L); - Context.Device.Log.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!"); + Logger.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!"); return MakeError(ErrorModule.Audio, AudErr.UnsupportedRevision); } @@ -157,13 +162,26 @@ namespace Ryujinx.HLE.HOS.Services.Aud return Result / 8; } - public long GetAudioDevice(ServiceCtx Context) + // GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object + public long GetAudioDeviceService(ServiceCtx Context) { - long UserId = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - MakeObject(Context, new IAudioDevice()); + MakeObject(Context, new IAudioDevice(Context.Device.System)); return 0; } + + // GetAudioDeviceServiceWithRevisionInfo(nn::applet::AppletResourceUserId, u32) -> object + private long GetAudioDeviceServiceWithRevisionInfo(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int RevisionInfo = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceAudio, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"RevisionInfo: {RevisionInfo}"); + + return GetAudioDeviceService(Context); + } } } diff --git a/Ryujinx.HLE/HOS/Services/Aud/IHardwareOpusDecoder.cs b/Ryujinx.HLE/HOS/Services/Aud/IHardwareOpusDecoder.cs new file mode 100644 index 0000000000..a71b8602ad --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Aud/IHardwareOpusDecoder.cs @@ -0,0 +1,91 @@ +using Concentus.Structs; +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +using static Ryujinx.HLE.HOS.ErrorCode; + +namespace Ryujinx.HLE.HOS.Services.Aud +{ + class IHardwareOpusDecoder : IpcService + { + private const int FixedSampleRate = 48000; + + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private int SampleRate; + private int ChannelsCount; + + private OpusDecoder Decoder; + + public IHardwareOpusDecoder(int SampleRate, int ChannelsCount) + { + m_Commands = new Dictionary() + { + { 0, DecodeInterleaved }, + { 4, DecodeInterleavedWithPerf } + }; + + this.SampleRate = SampleRate; + this.ChannelsCount = ChannelsCount; + + Decoder = new OpusDecoder(FixedSampleRate, ChannelsCount); + } + + public long DecodeInterleavedWithPerf(ServiceCtx Context) + { + long Result = DecodeInterleaved(Context); + + //TODO: Figure out what this value is. + //According to switchbrew, it is now used. + Context.ResponseData.Write(0L); + + return Result; + } + + public long DecodeInterleaved(ServiceCtx Context) + { + long InPosition = Context.Request.SendBuff[0].Position; + long InSize = Context.Request.SendBuff[0].Size; + + if (InSize < 8) + { + return MakeError(ErrorModule.Audio, AudErr.OpusInvalidInput); + } + + long OutPosition = Context.Request.ReceiveBuff[0].Position; + long OutSize = Context.Request.ReceiveBuff[0].Size; + + byte[] OpusData = Context.Memory.ReadBytes(InPosition, InSize); + + int Processed = ((OpusData[0] << 24) | + (OpusData[1] << 16) | + (OpusData[2] << 8) | + (OpusData[3] << 0)) + 8; + + if ((uint)Processed > (ulong)InSize) + { + return MakeError(ErrorModule.Audio, AudErr.OpusInvalidInput); + } + + short[] Pcm = new short[OutSize / 2]; + + int FrameSize = Pcm.Length / (ChannelsCount * 2); + + int Samples = Decoder.Decode(OpusData, 0, OpusData.Length, Pcm, 0, FrameSize); + + foreach (short Sample in Pcm) + { + Context.Memory.WriteInt16(OutPosition, Sample); + + OutPosition += 2; + } + + Context.ResponseData.Write(Processed); + Context.ResponseData.Write(Samples); + + return 0; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Aud/IHardwareOpusDecoderManager.cs b/Ryujinx.HLE/HOS/Services/Aud/IHardwareOpusDecoderManager.cs new file mode 100644 index 0000000000..875dc74c34 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Aud/IHardwareOpusDecoderManager.cs @@ -0,0 +1,72 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Aud +{ + class IHardwareOpusDecoderManager : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public IHardwareOpusDecoderManager() + { + m_Commands = new Dictionary() + { + { 0, Initialize }, + { 1, GetWorkBufferSize } + }; + } + + public long Initialize(ServiceCtx Context) + { + int SampleRate = Context.RequestData.ReadInt32(); + int ChannelsCount = Context.RequestData.ReadInt32(); + + MakeObject(Context, new IHardwareOpusDecoder(SampleRate, ChannelsCount)); + + return 0; + } + + public long GetWorkBufferSize(ServiceCtx Context) + { + //Note: The sample rate is ignored because it is fixed to 48KHz. + int SampleRate = Context.RequestData.ReadInt32(); + int ChannelsCount = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(GetOpusDecoderSize(ChannelsCount)); + + return 0; + } + + private static int GetOpusDecoderSize(int ChannelsCount) + { + const int SilkDecoderSize = 0x2198; + + if (ChannelsCount < 1 || ChannelsCount > 2) + { + return 0; + } + + int CeltDecoderSize = GetCeltDecoderSize(ChannelsCount); + + int OpusDecoderSize = (ChannelsCount * 0x800 + 0x4807) & -0x800 | 0x50; + + return OpusDecoderSize + SilkDecoderSize + CeltDecoderSize; + } + + private static int GetCeltDecoderSize(int ChannelsCount) + { + const int DecodeBufferSize = 0x2030; + const int CeltDecoderSize = 0x58; + const int CeltSigSize = 0x4; + const int Overlap = 120; + const int EBandsCount = 21; + + return (DecodeBufferSize + Overlap * 4) * ChannelsCount + + EBandsCount * 16 + + CeltDecoderSize + + CeltSigSize; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Bsd/BsdError.cs b/Ryujinx.HLE/HOS/Services/Bsd/BsdError.cs deleted file mode 100644 index 675edcc3dd..0000000000 --- a/Ryujinx.HLE/HOS/Services/Bsd/BsdError.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Bsd -{ - //bsd_errno == (SocketException.ErrorCode - 10000) - enum BsdError - { - Timeout = 60 - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Bsd/BsdIoctl.cs b/Ryujinx.HLE/HOS/Services/Bsd/BsdIoctl.cs new file mode 100644 index 0000000000..15fc7a8278 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Bsd/BsdIoctl.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Bsd +{ + enum BsdIoctl + { + AtMark = 0x40047307, + } +} diff --git a/Ryujinx.HLE/HOS/Services/Bsd/BsdSocket.cs b/Ryujinx.HLE/HOS/Services/Bsd/BsdSocket.cs index 2361ed31b7..2786da1364 100644 --- a/Ryujinx.HLE/HOS/Services/Bsd/BsdSocket.cs +++ b/Ryujinx.HLE/HOS/Services/Bsd/BsdSocket.cs @@ -1,4 +1,3 @@ -using System.Net; using System.Net.Sockets; namespace Ryujinx.HLE.HOS.Services.Bsd @@ -9,10 +8,6 @@ namespace Ryujinx.HLE.HOS.Services.Bsd public int Type; public int Protocol; - public IPAddress IpAddress; - - public IPEndPoint RemoteEP; - public Socket Handle; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Bsd/IClient.cs index e2cd0dcdb7..37d0fcfdba 100644 --- a/Ryujinx.HLE/HOS/Services/Bsd/IClient.cs +++ b/Ryujinx.HLE/HOS/Services/Bsd/IClient.cs @@ -1,45 +1,265 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.Utilities; using System.Collections.Generic; -using System.IO; using System.Net; using System.Net.Sockets; -using System.Threading.Tasks; +using System.Text; namespace Ryujinx.HLE.HOS.Services.Bsd { class IClient : IpcService { + + private static Dictionary ErrorMap = new Dictionary + { + // WSAEINTR + {WSAError.WSAEINTR, LinuxError.EINTR}, + // WSAEWOULDBLOCK + {WSAError.WSAEWOULDBLOCK, LinuxError.EWOULDBLOCK}, + // WSAEINPROGRESS + {WSAError.WSAEINPROGRESS, LinuxError.EINPROGRESS}, + // WSAEALREADY + {WSAError.WSAEALREADY, LinuxError.EALREADY}, + // WSAENOTSOCK + {WSAError.WSAENOTSOCK, LinuxError.ENOTSOCK}, + // WSAEDESTADDRREQ + {WSAError.WSAEDESTADDRREQ, LinuxError.EDESTADDRREQ}, + // WSAEMSGSIZE + {WSAError.WSAEMSGSIZE, LinuxError.EMSGSIZE}, + // WSAEPROTOTYPE + {WSAError.WSAEPROTOTYPE, LinuxError.EPROTOTYPE}, + // WSAENOPROTOOPT + {WSAError.WSAENOPROTOOPT, LinuxError.ENOPROTOOPT}, + // WSAEPROTONOSUPPORT + {WSAError.WSAEPROTONOSUPPORT, LinuxError.EPROTONOSUPPORT}, + // WSAESOCKTNOSUPPORT + {WSAError.WSAESOCKTNOSUPPORT, LinuxError.ESOCKTNOSUPPORT}, + // WSAEOPNOTSUPP + {WSAError.WSAEOPNOTSUPP, LinuxError.EOPNOTSUPP}, + // WSAEPFNOSUPPORT + {WSAError.WSAEPFNOSUPPORT, LinuxError.EPFNOSUPPORT}, + // WSAEAFNOSUPPORT + {WSAError.WSAEAFNOSUPPORT, LinuxError.EAFNOSUPPORT}, + // WSAEADDRINUSE + {WSAError.WSAEADDRINUSE, LinuxError.EADDRINUSE}, + // WSAEADDRNOTAVAIL + {WSAError.WSAEADDRNOTAVAIL, LinuxError.EADDRNOTAVAIL}, + // WSAENETDOWN + {WSAError.WSAENETDOWN, LinuxError.ENETDOWN}, + // WSAENETUNREACH + {WSAError.WSAENETUNREACH, LinuxError.ENETUNREACH}, + // WSAENETRESET + {WSAError.WSAENETRESET, LinuxError.ENETRESET}, + // WSAECONNABORTED + {WSAError.WSAECONNABORTED, LinuxError.ECONNABORTED}, + // WSAECONNRESET + {WSAError.WSAECONNRESET, LinuxError.ECONNRESET}, + // WSAENOBUFS + {WSAError.WSAENOBUFS, LinuxError.ENOBUFS}, + // WSAEISCONN + {WSAError.WSAEISCONN, LinuxError.EISCONN}, + // WSAENOTCONN + {WSAError.WSAENOTCONN, LinuxError.ENOTCONN}, + // WSAESHUTDOWN + {WSAError.WSAESHUTDOWN, LinuxError.ESHUTDOWN}, + // WSAETOOMANYREFS + {WSAError.WSAETOOMANYREFS, LinuxError.ETOOMANYREFS}, + // WSAETIMEDOUT + {WSAError.WSAETIMEDOUT, LinuxError.ETIMEDOUT}, + // WSAECONNREFUSED + {WSAError.WSAECONNREFUSED, LinuxError.ECONNREFUSED}, + // WSAELOOP + {WSAError.WSAELOOP, LinuxError.ELOOP}, + // WSAENAMETOOLONG + {WSAError.WSAENAMETOOLONG, LinuxError.ENAMETOOLONG}, + // WSAEHOSTDOWN + {WSAError.WSAEHOSTDOWN, LinuxError.EHOSTDOWN}, + // WSAEHOSTUNREACH + {WSAError.WSAEHOSTUNREACH, LinuxError.EHOSTUNREACH}, + // WSAENOTEMPTY + {WSAError.WSAENOTEMPTY, LinuxError.ENOTEMPTY}, + // WSAEUSERS + {WSAError.WSAEUSERS, LinuxError.EUSERS}, + // WSAEDQUOT + {WSAError.WSAEDQUOT, LinuxError.EDQUOT}, + // WSAESTALE + {WSAError.WSAESTALE, LinuxError.ESTALE}, + // WSAEREMOTE + {WSAError.WSAEREMOTE, LinuxError.EREMOTE}, + // WSAEINVAL + {WSAError.WSAEINVAL, LinuxError.EINVAL}, + // WSAEFAULT + {WSAError.WSAEFAULT, LinuxError.EFAULT}, + // NOERROR + {0, 0} + }; + private Dictionary m_Commands; public override IReadOnlyDictionary Commands => m_Commands; + private bool IsPrivileged; + private List Sockets = new List(); - public IClient() + public IClient(bool IsPrivileged) { m_Commands = new Dictionary() { - { 0, Initialize }, - { 1, StartMonitoring }, - { 2, Socket }, - { 6, Poll }, - { 8, Recv }, - { 10, Send }, - { 11, SendTo }, - { 12, Accept }, - { 13, Bind }, - { 14, Connect }, - { 18, Listen }, - { 21, SetSockOpt }, - { 24, Write }, - { 25, Read }, - { 26, Close } + { 0, RegisterClient }, + { 1, StartMonitoring }, + { 2, Socket }, + { 3, SocketExempt }, + { 4, Open }, + { 5, Select }, + { 6, Poll }, + { 7, Sysctl }, + { 8, Recv }, + { 9, RecvFrom }, + { 10, Send }, + { 11, SendTo }, + { 12, Accept }, + { 13, Bind }, + { 14, Connect }, + { 15, GetPeerName }, + { 16, GetSockName }, + { 17, GetSockOpt }, + { 18, Listen }, + { 19, Ioctl }, + { 20, Fcntl }, + { 21, SetSockOpt }, + { 22, Shutdown }, + { 23, ShutdownAllSockets }, + { 24, Write }, + { 25, Read }, + { 26, Close }, + { 27, DuplicateSocket }, }; + + this.IsPrivileged = IsPrivileged; } - //(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno - public long Initialize(ServiceCtx Context) + private LinuxError ConvertError(WSAError ErrorCode) + { + LinuxError Errno; + + if (!ErrorMap.TryGetValue(ErrorCode, out Errno)) + { + Errno = (LinuxError)ErrorCode; + } + + return Errno; + } + + private long WriteWinSock2Error(ServiceCtx Context, WSAError ErrorCode) + { + return WriteBsdResult(Context, -1, ConvertError(ErrorCode)); + } + + private long WriteBsdResult(ServiceCtx Context, int Result, LinuxError ErrorCode = 0) + { + if (ErrorCode != LinuxError.SUCCESS) + { + Result = -1; + } + + Context.ResponseData.Write(Result); + Context.ResponseData.Write((int)ErrorCode); + + return 0; + } + + private BsdSocket RetrieveSocket(int SocketFd) + { + if (SocketFd >= 0 && Sockets.Count > SocketFd) + { + return Sockets[SocketFd]; + } + + return null; + } + + private LinuxError SetResultErrno(Socket Socket, int Result) + { + return Result == 0 && !Socket.Blocking ? LinuxError.EWOULDBLOCK : LinuxError.SUCCESS; + } + + private AddressFamily ConvertFromBsd(int Domain) + { + if (Domain == 2) + { + return AddressFamily.InterNetwork; + } + + // FIXME: AF_ROUTE ignored, is that really needed? + return AddressFamily.Unknown; + } + + private long SocketInternal(ServiceCtx Context, bool Exempt) + { + AddressFamily Domain = (AddressFamily)Context.RequestData.ReadInt32(); + SocketType Type = (SocketType)Context.RequestData.ReadInt32(); + ProtocolType Protocol = (ProtocolType)Context.RequestData.ReadInt32(); + + if (Domain == AddressFamily.Unknown) + { + return WriteBsdResult(Context, -1, LinuxError.EPROTONOSUPPORT); + } + else if ((Type == SocketType.Seqpacket || Type == SocketType.Raw) && !IsPrivileged) + { + if (Domain != AddressFamily.InterNetwork || Type != SocketType.Raw || Protocol != ProtocolType.Icmp) + { + return WriteBsdResult(Context, -1, LinuxError.ENOENT); + } + } + + BsdSocket NewBsdSocket = new BsdSocket + { + Family = (int)Domain, + Type = (int)Type, + Protocol = (int)Protocol, + Handle = new Socket(Domain, Type, Protocol) + }; + + Sockets.Add(NewBsdSocket); + + if (Exempt) + { + NewBsdSocket.Handle.Disconnect(true); + } + + return WriteBsdResult(Context, Sockets.Count - 1); + } + + private IPEndPoint ParseSockAddr(ServiceCtx Context, long BufferPosition, long BufferSize) + { + int Size = Context.Memory.ReadByte(BufferPosition); + int Family = Context.Memory.ReadByte(BufferPosition + 1); + int Port = EndianSwap.Swap16(Context.Memory.ReadUInt16(BufferPosition + 2)); + + byte[] RawIp = Context.Memory.ReadBytes(BufferPosition + 4, 4); + + return new IPEndPoint(new IPAddress(RawIp), Port); + } + + private void WriteSockAddr(ServiceCtx Context, long BufferPosition, IPEndPoint EndPoint) + { + Context.Memory.WriteByte(BufferPosition, 0); + Context.Memory.WriteByte(BufferPosition + 1, (byte)EndPoint.AddressFamily); + Context.Memory.WriteUInt16(BufferPosition + 2, EndianSwap.Swap16((ushort)EndPoint.Port)); + Context.Memory.WriteBytes(BufferPosition + 4, EndPoint.Address.GetAddressBytes()); + } + + private void WriteSockAddr(ServiceCtx Context, long BufferPosition, BsdSocket Socket, bool IsRemote) + { + IPEndPoint EndPoint = (IsRemote ? Socket.Handle.RemoteEndPoint : Socket.Handle.LocalEndPoint) as IPEndPoint; + + WriteSockAddr(Context, BufferPosition, EndPoint); + } + + // Initialize(nn::socket::BsdBufferConfig config, u64 pid, u64 transferMemorySize, KObject, pid) -> u32 bsd_errno + public long RegisterClient(ServiceCtx Context) { /* typedef struct { @@ -54,447 +274,920 @@ namespace Ryujinx.HLE.HOS.Services.Bsd } BsdBufferConfig; */ + // bsd_error Context.ResponseData.Write(0); - //Todo: Stub + Logger.PrintStub(LogClass.ServiceBsd, "Stubbed."); return 0; } - //(u64, pid) + // StartMonitoring(u64, pid) public long StartMonitoring(ServiceCtx Context) { - //Todo: Stub + ulong Unknown0 = Context.RequestData.ReadUInt64(); + + Logger.PrintStub(LogClass.ServiceBsd, $"Stubbed. Unknown0: {Unknown0}"); return 0; } - //(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) + // Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) public long Socket(ServiceCtx Context) { - BsdSocket NewBsdSocket = new BsdSocket - { - Family = Context.RequestData.ReadInt32(), - Type = Context.RequestData.ReadInt32(), - Protocol = Context.RequestData.ReadInt32() - }; + return SocketInternal(Context, false); + } - Sockets.Add(NewBsdSocket); + // SocketExempt(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) + public long SocketExempt(ServiceCtx Context) + { + return SocketInternal(Context, true); + } - NewBsdSocket.Handle = new Socket((AddressFamily)NewBsdSocket.Family, - (SocketType)NewBsdSocket.Type, - (ProtocolType)NewBsdSocket.Protocol); + // Open(u32 flags, array path) -> (i32 ret, u32 bsd_errno) + public long Open(ServiceCtx Context) + { + (long BufferPosition, long BufferSize) = Context.Request.GetBufferType0x21(); - Context.ResponseData.Write(Sockets.Count - 1); - Context.ResponseData.Write(0); + int Flags = Context.RequestData.ReadInt32(); + + byte[] RawPath = Context.Memory.ReadBytes(BufferPosition, BufferSize); + string Path = Encoding.ASCII.GetString(RawPath); + + WriteBsdResult(Context, -1, LinuxError.EOPNOTSUPP); + + Logger.PrintStub(LogClass.ServiceBsd, $"Stubbed. Path: {Path} - " + + $"Flags: {Flags}"); return 0; } - //(u32, u32, buffer) -> (i32 ret, u32 bsd_errno, buffer) + // Select(u32 nfds, nn::socket::timeout timeout, buffer readfds_in, buffer writefds_in, buffer errorfds_in) -> (i32 ret, u32 bsd_errno, buffer readfds_out, buffer writefds_out, buffer errorfds_out) + public long Select(ServiceCtx Context) + { + WriteBsdResult(Context, -1, LinuxError.EOPNOTSUPP); + + Logger.PrintStub(LogClass.ServiceBsd, $"Stubbed."); + + return 0; + } + + // Poll(u32 nfds, u32 timeout, buffer fds) -> (i32 ret, u32 bsd_errno, buffer) public long Poll(ServiceCtx Context) { - int PollCount = Context.RequestData.ReadInt32(); - int TimeOut = Context.RequestData.ReadInt32(); + int FdsCount = Context.RequestData.ReadInt32(); + int Timeout = Context.RequestData.ReadInt32(); - //https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h - //https://msdn.microsoft.com/fr-fr/library/system.net.sockets.socket.poll(v=vs.110).aspx - //https://github.com/switchbrew/libnx/blob/e0457c4534b3c37426d83e1a620f82cb28c3b528/nx/source/services/bsd.c#L343 - //https://github.com/TuxSH/ftpd/blob/switch_pr/source/ftp.c#L1634 - //https://linux.die.net/man/2/poll + (long BufferPosition, long BufferSize) = Context.Request.GetBufferType0x21(); - byte[] SentBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, - Context.Request.SendBuff[0].Size); - int SocketId = Get32(SentBuffer, 0); - int RequestedEvents = Get16(SentBuffer, 4); - int ReturnedEvents = Get16(SentBuffer, 6); + if (Timeout < -1 || FdsCount < 0 || (FdsCount * 8) > BufferSize) + { + return WriteBsdResult(Context, -1, LinuxError.EINVAL); + } - //Todo: Stub - Need to implemented the Type-22 buffer. + PollEvent[] Events = new PollEvent[FdsCount]; - Context.ResponseData.Write(1); - Context.ResponseData.Write(0); + for (int i = 0; i < FdsCount; i++) + { + int SocketFd = Context.Memory.ReadInt32(BufferPosition + i * 8); + + BsdSocket Socket = RetrieveSocket(SocketFd); + + if (Socket == null) + { + return WriteBsdResult(Context, -1, LinuxError.EBADF); + } + + PollEvent.EventTypeMask InputEvents = (PollEvent.EventTypeMask)Context.Memory.ReadInt16(BufferPosition + i * 8 + 4); + PollEvent.EventTypeMask OutputEvents = (PollEvent.EventTypeMask)Context.Memory.ReadInt16(BufferPosition + i * 8 + 6); + + Events[i] = new PollEvent(SocketFd, Socket, InputEvents, OutputEvents); + } + + List ReadEvents = new List(); + List WriteEvents = new List(); + List ErrorEvents = new List(); + + foreach (PollEvent Event in Events) + { + bool IsValidEvent = false; + + if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0) + { + ReadEvents.Add(Event.Socket.Handle); + ErrorEvents.Add(Event.Socket.Handle); + + IsValidEvent = true; + } + + if ((Event.InputEvents & PollEvent.EventTypeMask.UrgentInput) != 0) + { + ReadEvents.Add(Event.Socket.Handle); + ErrorEvents.Add(Event.Socket.Handle); + + IsValidEvent = true; + } + + if ((Event.InputEvents & PollEvent.EventTypeMask.Output) != 0) + { + WriteEvents.Add(Event.Socket.Handle); + ErrorEvents.Add(Event.Socket.Handle); + + IsValidEvent = true; + } + + if ((Event.InputEvents & PollEvent.EventTypeMask.Error) != 0) + { + ErrorEvents.Add(Event.Socket.Handle); + IsValidEvent = true; + } + + if (!IsValidEvent) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Poll input event type: {Event.InputEvents}"); + return WriteBsdResult(Context, -1, LinuxError.EINVAL); + } + } + + try + { + System.Net.Sockets.Socket.Select(ReadEvents, WriteEvents, ErrorEvents, Timeout); + } + catch (SocketException Exception) + { + return WriteWinSock2Error(Context, (WSAError)Exception.ErrorCode); + } + + for (int i = 0; i < FdsCount; i++) + { + PollEvent Event = Events[i]; + Context.Memory.WriteInt32(BufferPosition + i * 8, Event.SocketFd); + Context.Memory.WriteInt16(BufferPosition + i * 8 + 4, (short)Event.InputEvents); + + PollEvent.EventTypeMask OutputEvents = 0; + + Socket Socket = Event.Socket.Handle; + + if (ErrorEvents.Contains(Socket)) + { + OutputEvents |= PollEvent.EventTypeMask.Error; + + if (!Socket.Connected || !Socket.IsBound) + { + OutputEvents |= PollEvent.EventTypeMask.Disconnected; + } + } + + if (ReadEvents.Contains(Socket)) + { + if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0) + { + OutputEvents |= PollEvent.EventTypeMask.Input; + } + } + + if (WriteEvents.Contains(Socket)) + { + OutputEvents |= PollEvent.EventTypeMask.Output; + } + + Context.Memory.WriteInt16(BufferPosition + i * 8 + 6, (short)OutputEvents); + } + + return WriteBsdResult(Context, ReadEvents.Count + WriteEvents.Count + ErrorEvents.Count, LinuxError.SUCCESS); + } + + // Sysctl(buffer, buffer) -> (i32 ret, u32 bsd_errno, u32, buffer) + public long Sysctl(ServiceCtx Context) + { + WriteBsdResult(Context, -1, LinuxError.EOPNOTSUPP); + + Logger.PrintStub(LogClass.ServiceBsd, $"Stubbed."); return 0; } - //(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, buffer message) + // Recv(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, array message) public long Recv(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); - int SocketFlags = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); + SocketFlags SocketFlags = (SocketFlags)Context.RequestData.ReadInt32(); (long ReceivePosition, long ReceiveLength) = Context.Request.GetBufferType0x22(); - byte[] ReceivedBuffer = new byte[ReceiveLength]; + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + int Result = -1; - try + if (Socket != null) { - int BytesRead = Sockets[SocketId].Handle.Receive(ReceivedBuffer); + if (SocketFlags != SocketFlags.None && (SocketFlags & SocketFlags.OutOfBand) == 0 + && (SocketFlags & SocketFlags.Peek) == 0) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Recv flags: {SocketFlags}"); + return WriteBsdResult(Context, -1, LinuxError.EOPNOTSUPP); + } - Context.Memory.WriteBytes(ReceivePosition, ReceivedBuffer); + byte[] ReceivedBuffer = new byte[ReceiveLength]; - Context.ResponseData.Write(BytesRead); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + try + { + Result = Socket.Handle.Receive(ReceivedBuffer, SocketFlags); + Errno = SetResultErrno(Socket.Handle, Result); + + Context.Memory.WriteBytes(ReceivePosition, ReceivedBuffer); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } } - return 0; + return WriteBsdResult(Context, Result, Errno); } - //(u32 socket, u32 flags, buffer) -> (i32 ret, u32 bsd_errno) + // RecvFrom(u32 sock, u32 flags) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer message, buffer) + public long RecvFrom(ServiceCtx Context) + { + int SocketFd = Context.RequestData.ReadInt32(); + SocketFlags SocketFlags = (SocketFlags)Context.RequestData.ReadInt32(); + + (long ReceivePosition, long ReceiveLength) = Context.Request.GetBufferType0x22(); + (long SockAddrInPosition, long SockAddrInSize) = Context.Request.GetBufferType0x21(); + (long SockAddrOutPosition, long SockAddrOutSize) = Context.Request.GetBufferType0x22(1); + + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + int Result = -1; + + if (Socket != null) + { + if (SocketFlags != SocketFlags.None && (SocketFlags & SocketFlags.OutOfBand) == 0 + && (SocketFlags & SocketFlags.Peek) == 0) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Recv flags: {SocketFlags}"); + + return WriteBsdResult(Context, -1, LinuxError.EOPNOTSUPP); + } + + byte[] ReceivedBuffer = new byte[ReceiveLength]; + EndPoint EndPoint = ParseSockAddr(Context, SockAddrInPosition, SockAddrInSize); + + try + { + Result = Socket.Handle.ReceiveFrom(ReceivedBuffer, ReceivedBuffer.Length, SocketFlags, ref EndPoint); + Errno = SetResultErrno(Socket.Handle, Result); + + Context.Memory.WriteBytes(ReceivePosition, ReceivedBuffer); + WriteSockAddr(Context, SockAddrOutPosition, (IPEndPoint)EndPoint); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } + } + + return WriteBsdResult(Context, Result, Errno); + } + + // Send(u32 socket, u32 flags, buffer) -> (i32 ret, u32 bsd_errno) public long Send(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); - int SocketFlags = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); + SocketFlags SocketFlags = (SocketFlags)Context.RequestData.ReadInt32(); - (long SentPosition, long SentSize) = Context.Request.GetBufferType0x21(); + (long SendPosition, long SendSize) = Context.Request.GetBufferType0x21(); - byte[] SentBuffer = Context.Memory.ReadBytes(SentPosition, SentSize); + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + int Result = -1; - try + if (Socket != null) { - int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer); + if (SocketFlags != SocketFlags.None && SocketFlags != SocketFlags.OutOfBand + && SocketFlags != SocketFlags.Peek && SocketFlags != SocketFlags.DontRoute) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Send flags: {SocketFlags}"); + + return WriteBsdResult(Context, -1, LinuxError.EOPNOTSUPP); + } + + byte[] SendBuffer = Context.Memory.ReadBytes(SendPosition, SendSize); + + try + { + Result = Socket.Handle.Send(SendBuffer, SocketFlags); + Errno = SetResultErrno(Socket.Handle, Result); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } - Context.ResponseData.Write(BytesSent); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); } - return 0; + return WriteBsdResult(Context, Result, Errno); } - //(u32 socket, u32 flags, buffer, buffer) -> (i32 ret, u32 bsd_errno) + // SendTo(u32 socket, u32 flags, buffer, buffer) -> (i32 ret, u32 bsd_errno) public long SendTo(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); - int SocketFlags = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); + SocketFlags SocketFlags = (SocketFlags)Context.RequestData.ReadInt32(); - byte[] SentBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, - Context.Request.SendBuff[0].Size); + (long SendPosition, long SendSize) = Context.Request.GetBufferType0x21(); + (long BufferPosition, long BufferSize) = Context.Request.GetBufferType0x21(1); - (long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21(Index: 1); + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + int Result = -1; - byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize); - - if (!Sockets[SocketId].Handle.Connected) + if (Socket != null) { + if (SocketFlags != SocketFlags.None && SocketFlags != SocketFlags.OutOfBand + && SocketFlags != SocketFlags.Peek && SocketFlags != SocketFlags.DontRoute) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Send flags: {SocketFlags}"); + + return WriteBsdResult(Context, -1, LinuxError.EOPNOTSUPP); + } + + byte[] SendBuffer = Context.Memory.ReadBytes(SendPosition, SendSize); + EndPoint EndPoint = ParseSockAddr(Context, BufferPosition, BufferSize); + try { - ParseAddrBuffer(SocketId, AddressBuffer); - - Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP); + Result = Socket.Handle.SendTo(SendBuffer, SendBuffer.Length, SocketFlags, EndPoint); + Errno = SetResultErrno(Socket.Handle, Result); } - catch (SocketException Ex) + catch (SocketException Exception) { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + Errno = ConvertError((WSAError)Exception.ErrorCode); } + } - try - { - int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer); - - Context.ResponseData.Write(BytesSent); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); - } - - return 0; + return WriteBsdResult(Context, Result, Errno); } - //(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer addr) + // Accept(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer addr) public long Accept(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); - (long AddrBufferPosition, long AddrBuffSize) = Context.Request.GetBufferType0x22(); + (long BufferPos, long BufferSize) = Context.Request.GetBufferType0x22(); - Socket HandleAccept = null; + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); - Task TimeOut = Task.Factory.StartNew(() => + if (Socket != null) { + Errno = LinuxError.SUCCESS; + + Socket NewSocket = null; + try { - HandleAccept = Sockets[SocketId].Handle.Accept(); + NewSocket = Socket.Handle.Accept(); } - catch (SocketException Ex) + catch (SocketException Exception) { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + Errno = ConvertError((WSAError)Exception.ErrorCode); } - }); - TimeOut.Wait(10000); - - if (HandleAccept != null) - { - BsdSocket NewBsdSocket = new BsdSocket + if (NewSocket == null && Errno == LinuxError.SUCCESS) { - IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address, - RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint), - Handle = HandleAccept - }; - - Sockets.Add(NewBsdSocket); - - using (MemoryStream MS = new MemoryStream()) + Errno = LinuxError.EWOULDBLOCK; + } + else if (Errno == LinuxError.SUCCESS) { - BinaryWriter Writer = new BinaryWriter(MS); + BsdSocket NewBsdSocket = new BsdSocket + { + Family = (int)NewSocket.AddressFamily, + Type = (int)NewSocket.SocketType, + Protocol = (int)NewSocket.ProtocolType, + Handle = NewSocket, + }; - Writer.Write((byte)0); + Sockets.Add(NewBsdSocket); - Writer.Write((byte)NewBsdSocket.Handle.AddressFamily); + WriteSockAddr(Context, BufferPos, NewBsdSocket, true); - Writer.Write((short)((IPEndPoint)NewBsdSocket.Handle.LocalEndPoint).Port); + WriteBsdResult(Context, Sockets.Count - 1, Errno); - byte[] IpAddress = NewBsdSocket.IpAddress.GetAddressBytes(); + Context.ResponseData.Write(0x10); - Writer.Write(IpAddress); - - Context.Memory.WriteBytes(AddrBufferPosition, MS.ToArray()); - - Context.ResponseData.Write(Sockets.Count - 1); - Context.ResponseData.Write(0); - Context.ResponseData.Write(MS.Length); + return 0; } } - else - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write((int)BsdError.Timeout); - } - return 0; + return WriteBsdResult(Context, -1, Errno); } - //(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) + // Bind(u32 socket, buffer addr) -> (i32 ret, u32 bsd_errno) public long Bind(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); - (long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21(); + (long BufferPos, long BufferSize) = Context.Request.GetBufferType0x21(); - byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize); + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); - try + if (Socket != null) { - ParseAddrBuffer(SocketId, AddressBuffer); + Errno = LinuxError.SUCCESS; - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + try + { + IPEndPoint EndPoint = ParseSockAddr(Context, BufferPos, BufferSize); + + Socket.Handle.Bind(EndPoint); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } } - return 0; + return WriteBsdResult(Context, 0, Errno); } - //(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) + // Connect(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) public long Connect(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); - (long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21(); + (long BufferPos, long BufferSize) = Context.Request.GetBufferType0x21(); - byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize); + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); - try + if (Socket != null) { - ParseAddrBuffer(SocketId, AddressBuffer); + Errno = LinuxError.SUCCESS; + try + { + IPEndPoint EndPoint = ParseSockAddr(Context, BufferPos, BufferSize); - Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP); - - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + Socket.Handle.Connect(EndPoint); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } } - return 0; + return WriteBsdResult(Context, 0, Errno); } - //(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno) + // GetPeerName(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer addr) + public long GetPeerName(ServiceCtx Context) + { + int SocketFd = Context.RequestData.ReadInt32(); + + (long BufferPos, long BufferSize) = Context.Request.GetBufferType0x22(); + + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + + if (Socket != null) + { + Errno = LinuxError.SUCCESS; + + WriteSockAddr(Context, BufferPos, Socket, true); + WriteBsdResult(Context, 0, Errno); + Context.ResponseData.Write(0x10); + } + + return WriteBsdResult(Context, 0, Errno); + } + + // GetSockName(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer addr) + public long GetSockName(ServiceCtx Context) + { + int SocketFd = Context.RequestData.ReadInt32(); + + (long BufferPos, long BufferSize) = Context.Request.GetBufferType0x22(); + + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + + if (Socket != null) + { + Errno = LinuxError.SUCCESS; + + WriteSockAddr(Context, BufferPos, Socket, false); + WriteBsdResult(Context, 0, Errno); + Context.ResponseData.Write(0x10); + } + + return WriteBsdResult(Context, 0, Errno); + } + + // GetSockOpt(u32 socket, u32 level, u32 option_name) -> (i32 ret, u32 bsd_errno, u32, buffer) + public long GetSockOpt(ServiceCtx Context) + { + int SocketFd = Context.RequestData.ReadInt32(); + int Level = Context.RequestData.ReadInt32(); + int OptionName = Context.RequestData.ReadInt32(); + + (long BufferPosition, long BufferSize) = Context.Request.GetBufferType0x22(); + + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + + if (Socket != null) + { + Errno = LinuxError.ENOPROTOOPT; + + if (Level == 0xFFFF) + { + Errno = HandleGetSocketOption(Context, Socket, (SocketOptionName)OptionName, BufferPosition, BufferSize); + } + else + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported GetSockOpt Level: {(SocketOptionLevel)Level}"); + } + } + + return WriteBsdResult(Context, 0, Errno); + } + + // Listen(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno) public long Listen(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); - int BackLog = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); + int Backlog = Context.RequestData.ReadInt32(); - try - { - Sockets[SocketId].Handle.Bind(Sockets[SocketId].RemoteEP); - Sockets[SocketId].Handle.Listen(BackLog); + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) + if (Socket != null) { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + Errno = LinuxError.SUCCESS; + + try + { + Socket.Handle.Listen(Backlog); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } } - return 0; + return WriteBsdResult(Context, 0, Errno); } - //(u32 socket, u32 level, u32 option_name, buffer) -> (i32 ret, u32 bsd_errno) + // Ioctl(u32 fd, u32 request, u32 bufcount, buffer, buffer, buffer, buffer) -> (i32 ret, u32 bsd_errno, buffer, buffer, buffer, buffer) + public long Ioctl(ServiceCtx Context) + { + int SocketFd = Context.RequestData.ReadInt32(); + BsdIoctl Cmd = (BsdIoctl)Context.RequestData.ReadInt32(); + int BufferCount = Context.RequestData.ReadInt32(); + + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + + if (Socket != null) + { + switch (Cmd) + { + case BsdIoctl.AtMark: + Errno = LinuxError.SUCCESS; + + (long BufferPosition, long BufferSize) = Context.Request.GetBufferType0x22(); + + // FIXME: OOB not implemented. + Context.Memory.WriteInt32(BufferPosition, 0); + break; + + default: + Errno = LinuxError.EOPNOTSUPP; + + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Ioctl Cmd: {Cmd}"); + break; + } + } + + return WriteBsdResult(Context, 0, Errno); + } + + // Fcntl(u32 socket, u32 cmd, u32 arg) -> (i32 ret, u32 bsd_errno) + public long Fcntl(ServiceCtx Context) + { + int SocketFd = Context.RequestData.ReadInt32(); + int Cmd = Context.RequestData.ReadInt32(); + int Arg = Context.RequestData.ReadInt32(); + + int Result = 0; + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + + if (Socket != null) + { + Errno = LinuxError.SUCCESS; + + if (Cmd == 0x3) + { + Result = !Socket.Handle.Blocking ? 0x800 : 0; + } + else if (Cmd == 0x4 && Arg == 0x800) + { + Socket.Handle.Blocking = false; + Result = 0; + } + else + { + Errno = LinuxError.EOPNOTSUPP; + } + } + + return WriteBsdResult(Context, Result, Errno); + } + + private LinuxError HandleGetSocketOption(ServiceCtx Context, BsdSocket Socket, SocketOptionName OptionName, long OptionValuePosition, long OptionValueSize) + { + try + { + byte[] OptionValue = new byte[OptionValueSize]; + + switch (OptionName) + { + case SocketOptionName.Broadcast: + case SocketOptionName.DontLinger: + case SocketOptionName.Debug: + case SocketOptionName.Error: + case SocketOptionName.KeepAlive: + case SocketOptionName.OutOfBandInline: + case SocketOptionName.ReceiveBuffer: + case SocketOptionName.ReceiveTimeout: + case SocketOptionName.SendBuffer: + case SocketOptionName.SendTimeout: + case SocketOptionName.Type: + case SocketOptionName.Linger: + Socket.Handle.GetSocketOption(SocketOptionLevel.Socket, OptionName, OptionValue); + Context.Memory.WriteBytes(OptionValuePosition, OptionValue); + + return LinuxError.SUCCESS; + + case (SocketOptionName)0x200: + Socket.Handle.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, OptionValue); + Context.Memory.WriteBytes(OptionValuePosition, OptionValue); + + return LinuxError.SUCCESS; + + default: + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt OptionName: {OptionName}"); + + return LinuxError.EOPNOTSUPP; + } + } + catch (SocketException Exception) + { + return ConvertError((WSAError)Exception.ErrorCode); + } + } + + private LinuxError HandleSetSocketOption(ServiceCtx Context, BsdSocket Socket, SocketOptionName OptionName, long OptionValuePosition, long OptionValueSize) + { + try + { + switch (OptionName) + { + case SocketOptionName.Broadcast: + case SocketOptionName.DontLinger: + case SocketOptionName.Debug: + case SocketOptionName.Error: + case SocketOptionName.KeepAlive: + case SocketOptionName.OutOfBandInline: + case SocketOptionName.ReceiveBuffer: + case SocketOptionName.ReceiveTimeout: + case SocketOptionName.SendBuffer: + case SocketOptionName.SendTimeout: + case SocketOptionName.Type: + case SocketOptionName.ReuseAddress: + Socket.Handle.SetSocketOption(SocketOptionLevel.Socket, OptionName, Context.Memory.ReadInt32(OptionValuePosition)); + + return LinuxError.SUCCESS; + + case (SocketOptionName)0x200: + Socket.Handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, Context.Memory.ReadInt32(OptionValuePosition)); + + return LinuxError.SUCCESS; + + case SocketOptionName.Linger: + Socket.Handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, + new LingerOption(Context.Memory.ReadInt32(OptionValuePosition) != 0, Context.Memory.ReadInt32(OptionValuePosition + 4))); + + return LinuxError.SUCCESS; + + default: + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt OptionName: {OptionName}"); + + return LinuxError.EOPNOTSUPP; + } + } + catch (SocketException Exception) + { + return ConvertError((WSAError)Exception.ErrorCode); + } + } + + // SetSockOpt(u32 socket, u32 level, u32 option_name, buffer option_value) -> (i32 ret, u32 bsd_errno) public long SetSockOpt(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); + int Level = Context.RequestData.ReadInt32(); + int OptionName = Context.RequestData.ReadInt32(); - SocketOptionLevel SocketLevel = (SocketOptionLevel)Context.RequestData.ReadInt32(); - SocketOptionName SocketOptionName = (SocketOptionName)Context.RequestData.ReadInt32(); + (long BufferPos, long BufferSize) = Context.Request.GetBufferType0x21(); - byte[] SocketOptionValue = Context.Memory.ReadBytes(Context.Request.PtrBuff[0].Position, - Context.Request.PtrBuff[0].Size); + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); - int OptionValue = Get32(SocketOptionValue, 0); - - try + if (Socket != null) { - Sockets[SocketId].Handle.SetSocketOption(SocketLevel, SocketOptionName, OptionValue); + Errno = LinuxError.ENOPROTOOPT; - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + if (Level == 0xFFFF) + { + Errno = HandleSetSocketOption(Context, Socket, (SocketOptionName)OptionName, BufferPos, BufferSize); + } + else + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt Level: {(SocketOptionLevel)Level}"); + } } - return 0; + return WriteBsdResult(Context, 0, Errno); } - //(u32 socket, buffer message) -> (i32 ret, u32 bsd_errno) + // Shutdown(u32 socket, u32 how) -> (i32 ret, u32 bsd_errno) + public long Shutdown(ServiceCtx Context) + { + int SocketFd = Context.RequestData.ReadInt32(); + int How = Context.RequestData.ReadInt32(); + + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + + if (Socket != null) + { + Errno = LinuxError.EINVAL; + + if (How >= 0 && How <= 2) + { + Errno = LinuxError.SUCCESS; + + try + { + Socket.Handle.Shutdown((SocketShutdown)How); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } + } + } + + return WriteBsdResult(Context, 0, Errno); + } + + // ShutdownAllSockets(u32 how) -> (i32 ret, u32 bsd_errno) + public long ShutdownAllSockets(ServiceCtx Context) + { + int How = Context.RequestData.ReadInt32(); + + LinuxError Errno = LinuxError.EINVAL; + + if (How >= 0 && How <= 2) + { + Errno = LinuxError.SUCCESS; + + foreach (BsdSocket Socket in Sockets) + { + if (Socket != null) + { + try + { + Socket.Handle.Shutdown((SocketShutdown)How); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + break; + } + } + } + } + + return WriteBsdResult(Context, 0, Errno); + } + + // Write(u32 socket, buffer message) -> (i32 ret, u32 bsd_errno) public long Write(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); - (long SentPosition, long SentSize) = Context.Request.GetBufferType0x21(); + (long SendPosition, long SendSize) = Context.Request.GetBufferType0x21(); - byte[] SentBuffer = Context.Memory.ReadBytes(SentPosition, SentSize); + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + int Result = -1; - try + if (Socket != null) { - //Logging.Debug("Wrote Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer)); + byte[] SendBuffer = Context.Memory.ReadBytes(SendPosition, SendSize); - int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer); - - Context.ResponseData.Write(BytesSent); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + try + { + Result = Socket.Handle.Send(SendBuffer); + Errno = SetResultErrno(Socket.Handle, Result); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } } - return 0; + return WriteBsdResult(Context, Result, Errno); } - //(u32 socket) -> (i32 ret, u32 bsd_errno, buffer message) + // Read(u32 socket) -> (i32 ret, u32 bsd_errno, buffer message) public long Read(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); (long ReceivePosition, long ReceiveLength) = Context.Request.GetBufferType0x22(); - byte[] ReceivedBuffer = new byte[ReceiveLength]; + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); + int Result = -1; - try + if (Socket != null) { - int BytesRead = Sockets[SocketId].Handle.Receive(ReceivedBuffer); + byte[] ReceivedBuffer = new byte[ReceiveLength]; - Context.Memory.WriteBytes(ReceivePosition, ReceivedBuffer); - - Context.ResponseData.Write(BytesRead); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) - { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + try + { + Result = Socket.Handle.Receive(ReceivedBuffer); + Errno = SetResultErrno(Socket.Handle, Result); + } + catch (SocketException Exception) + { + Errno = ConvertError((WSAError)Exception.ErrorCode); + } } - return 0; + return WriteBsdResult(Context, Result, Errno); } - //(u32 socket) -> (i32 ret, u32 bsd_errno) + // Close(u32 socket) -> (i32 ret, u32 bsd_errno) public long Close(ServiceCtx Context) { - int SocketId = Context.RequestData.ReadInt32(); + int SocketFd = Context.RequestData.ReadInt32(); - try - { - Sockets[SocketId].Handle.Close(); - Sockets[SocketId] = null; + LinuxError Errno = LinuxError.EBADF; + BsdSocket Socket = RetrieveSocket(SocketFd); - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); - } - catch (SocketException Ex) + if (Socket != null) { - Context.ResponseData.Write(-1); - Context.ResponseData.Write(Ex.ErrorCode - 10000); + Socket.Handle.Close(); + + Sockets[SocketFd] = null; + + Errno = LinuxError.SUCCESS; } - return 0; + return WriteBsdResult(Context, 0, Errno); } - public void ParseAddrBuffer(int SocketId, byte[] AddrBuffer) + // DuplicateSocket(u32 socket, u64 reserved) -> (i32 ret, u32 bsd_errno) + public long DuplicateSocket(ServiceCtx Context) { - using (MemoryStream MS = new MemoryStream(AddrBuffer)) + int SocketFd = Context.RequestData.ReadInt32(); + ulong Reserved = Context.RequestData.ReadUInt64(); + + LinuxError Errno = LinuxError.ENOENT; + int NewSockFd = -1; + + if (IsPrivileged) { - BinaryReader Reader = new BinaryReader(MS); + Errno = LinuxError.EBADF; - int Size = Reader.ReadByte(); - int Family = Reader.ReadByte(); - int Port = EndianSwap.Swap16(Reader.ReadUInt16()); + BsdSocket OldSocket = RetrieveSocket(SocketFd); - string IpAddress = Reader.ReadByte().ToString() + "." + - Reader.ReadByte().ToString() + "." + - Reader.ReadByte().ToString() + "." + - Reader.ReadByte().ToString(); - - Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress); - Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port); + if (OldSocket != null) + { + Sockets.Add(OldSocket); + NewSockFd = Sockets.Count - 1; + } } - } - private int Get16(byte[] Data, int Address) - { - return - Data[Address + 0] << 0 | - Data[Address + 1] << 8; - } - - private int Get32(byte[] Data, int Address) - { - return - Data[Address + 0] << 0 | - Data[Address + 1] << 8 | - Data[Address + 2] << 16 | - Data[Address + 3] << 24; + return WriteBsdResult(Context, NewSockFd, Errno); } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Bsd/PollEvent.cs b/Ryujinx.HLE/HOS/Services/Bsd/PollEvent.cs new file mode 100644 index 0000000000..49cd4877ee --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Bsd/PollEvent.cs @@ -0,0 +1,28 @@ +namespace Ryujinx.HLE.HOS.Services.Bsd +{ + class PollEvent + { + public enum EventTypeMask + { + Input = 1, + UrgentInput = 2, + Output = 4, + Error = 8, + Disconnected = 0x10, + Invalid = 0x20, + } + + public int SocketFd { get; private set; } + public BsdSocket Socket { get; private set; } + public EventTypeMask InputEvents { get; private set; } + public EventTypeMask OutputEvents { get; private set; } + + public PollEvent(int SocketFd, BsdSocket Socket, EventTypeMask InputEvents, EventTypeMask OutputEvents) + { + this.SocketFd = SocketFd; + this.Socket = Socket; + this.InputEvents = InputEvents; + this.OutputEvents = OutputEvents; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs index 3b06ba8afc..e20de267ff 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs @@ -1,6 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Logging; +using Ryujinx.HLE.Utilities; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Friend @@ -15,14 +16,53 @@ namespace Ryujinx.HLE.HOS.Services.Friend { m_Commands = new Dictionary() { + { 10101, GetFriendList }, { 10601, DeclareCloseOnlinePlaySession }, { 10610, UpdateUserPresence } }; } + // nn::friends::GetFriendListGetFriendListIds(nn::account::Uid, int Unknown0, nn::friends::detail::ipc::SizedFriendFilter, ulong Unknown1) -> int CounterIds, array + public long GetFriendList(ServiceCtx Context) + { + UInt128 Uuid = new UInt128( + Context.RequestData.ReadInt64(), + Context.RequestData.ReadInt64()); + + int Unknown0 = Context.RequestData.ReadInt32(); + + FriendFilter Filter = new FriendFilter() + { + PresenceStatus = (PresenceStatusFilter)Context.RequestData.ReadInt32(), + IsFavoriteOnly = Context.RequestData.ReadBoolean(), + IsSameAppPresenceOnly = Context.RequestData.ReadBoolean(), + IsSameAppPlayedOnly = Context.RequestData.ReadBoolean(), + IsArbitraryAppPlayedOnly = Context.RequestData.ReadBoolean(), + PresenceGroupId = Context.RequestData.ReadInt64() + }; + + long Unknown1 = Context.RequestData.ReadInt64(); + + // There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty. + Context.ResponseData.Write(0); + + Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.ToString()} - " + + $"Unknown0: {Unknown0} - " + + $"PresenceStatus: {Filter.PresenceStatus} - " + + $"IsFavoriteOnly: {Filter.IsFavoriteOnly} - " + + $"IsSameAppPresenceOnly: {Filter.IsSameAppPresenceOnly} - " + + $"IsSameAppPlayedOnly: {Filter.IsSameAppPlayedOnly} - " + + $"IsArbitraryAppPlayedOnly: {Filter.IsArbitraryAppPlayedOnly} - " + + $"PresenceGroupId: {Filter.PresenceGroupId} - " + + $"Unknown1: {Unknown1}"); + + return 0; + } + + // DeclareCloseOnlinePlaySession(nn::account::Uid) public long DeclareCloseOnlinePlaySession(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); @@ -31,19 +71,30 @@ namespace Ryujinx.HLE.HOS.Services.Friend Profile.OnlinePlayState = OpenCloseState.Closed; } + Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " + + $"OnlinePlayState: {Profile.OnlinePlayState}"); + return 0; } + // UpdateUserPresence(nn::account::Uid, ulong Unknown0) -> buffer public long UpdateUserPresence(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); - //TODO. - Context.Device.Log.PrintStub(LogClass.ServiceFriend, "Stubbed."); + long Unknown0 = Context.RequestData.ReadInt64(); + + long Position = Context.Request.PtrBuff[0].Position; + long Size = Context.Request.PtrBuff[0].Size; + + //Todo: Write the buffer content. + + Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " + + $"Unknown0: {Unknown0}"); return 0; } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Friend/IFriendServiceTypes.cs b/Ryujinx.HLE/HOS/Services/Friend/IFriendServiceTypes.cs new file mode 100644 index 0000000000..31459f7d68 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Friend/IFriendServiceTypes.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.HLE.HOS.Services.Friend +{ + enum PresenceStatusFilter + { + None, + Online, + OnlinePlay, + OnlineOrOnlinePlay + } + + struct FriendFilter + { + public PresenceStatusFilter PresenceStatus; + public bool IsFavoriteOnly; + public bool IsSameAppPresenceOnly; + public bool IsSameAppPlayedOnly; + public bool IsArbitraryAppPlayedOnly; + public long PresenceGroupId; + } +} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs index b77043bddb..bd249e5084 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs @@ -281,11 +281,6 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (IsPathAlreadyInUse(DirName)) - { - return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); - } - IDirectory DirInterface = new IDirectory(DirName, FilterFlags); DirInterface.Disposed += RemoveDirectoryInUse; diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs index 937ea6d6bf..daf5e0b269 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.Utilities; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.FspSrv @@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv long TitleId = Context.RequestData.ReadInt64(); - UserId UserId = new UserId( + UInt128 UserId = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs index 5118fa457b..c3f1f2c4c4 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs @@ -39,8 +39,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv byte[] Data = new byte[Size]; - BaseStream.Seek(Offset, SeekOrigin.Begin); - BaseStream.Read(Data, 0, Data.Length); + lock (BaseStream) + { + BaseStream.Seek(Offset, SeekOrigin.Begin); + BaseStream.Read(Data, 0, Data.Length); + } Context.Memory.WriteBytes(BuffDesc.Position, Data); } @@ -48,4 +51,4 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs b/Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs new file mode 100644 index 0000000000..d273185733 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs @@ -0,0 +1,41 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public enum HidNpadJoyAssignmentMode + { + Dual, + Single, + } + + public enum HidNpadHandheldActivationMode + { + Dual, + Single, + None, + } + + public enum HidNpadJoyDeviceType + { + Left, + Right, + } + + public enum HidNpadJoyHoldType + { + Vertical, + Horizontal, + } + + [Flags] + public enum HidNpadStyle + { + None, + FullKey = 1 << 0, + Handheld = 1 << 1, + Dual = 1 << 2, + Left = 1 << 3, + Right = 1 << 4, + Invalid = 1 << 5, + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs b/Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs new file mode 100644 index 0000000000..bd0647621e --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public struct HidSensorFusionParameters + { + public float RevisePower; + public float ReviseRange; + } + + public struct HidAccelerometerParameters + { + public float X; + public float Y; + } + + public enum HidGyroscopeZeroDriftMode + { + Loose, + Standard, + Tight + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs b/Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs new file mode 100644 index 0000000000..cb2427e732 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public enum HidVibrationDeviceType + { + None, + LinearResonantActuator + } + + public enum HidVibrationDevicePosition + { + None, + Left, + Right, + } + + public struct HidVibrationDeviceValue + { + public HidVibrationDeviceType DeviceType; + public HidVibrationDevicePosition Position; + } + + public struct HidVibrationValue + { + public float AmplitudeLow; + public float FrequencyLow; + public float AmplitudeHigh; + public float FrequencyHigh; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/IAppletResource.cs b/Ryujinx.HLE/HOS/Services/Hid/IAppletResource.cs index 012ccb4053..89a17acf7b 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IAppletResource.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IAppletResource.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Hid @@ -24,7 +25,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid public long GetSharedMemoryHandle(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(HidSharedMem); + if (Context.Process.HandleTable.GenerateHandle(HidSharedMem, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index 70f1f1f1f5..e54ca8125d 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -1,274 +1,1034 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Input; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Hid { - class IHidServer : IpcService, IDisposable + class IHidServer : IpcService { private Dictionary m_Commands; private KEvent NpadStyleSetUpdateEvent; + private KEvent XpadIdEvent; + private KEvent PalmaOperationCompleteEvent; + + private int XpadIdEventHandle; + + private bool SixAxisSensorFusionEnabled; + private bool UnintendedHomeButtonInputProtectionEnabled; + private bool VibrationPermitted; + private bool UsbFullKeyControllerEnabled; + + private HidNpadJoyHoldType NpadJoyHoldType; + private HidNpadStyle NpadStyleTag; + private HidNpadJoyAssignmentMode NpadJoyAssignmentMode; + private HidNpadHandheldActivationMode NpadHandheldActivationMode; + private HidGyroscopeZeroDriftMode GyroscopeZeroDriftMode; + + private long NpadCommunicationMode; + private uint AccelerometerPlayMode; + private long VibrationGcErmCommand; + private float SevenSixAxisSensorFusionStrength; + + private HidSensorFusionParameters SensorFusionParams; + private HidAccelerometerParameters AccelerometerParams; + private HidVibrationValue VibrationValue; public override IReadOnlyDictionary Commands => m_Commands; - public IHidServer() + public IHidServer(Horizon System) { m_Commands = new Dictionary() { - { 0, CreateAppletResource }, - { 1, ActivateDebugPad }, - { 11, ActivateTouchScreen }, - { 21, ActivateMouse }, - { 31, ActivateKeyboard }, - { 66, StartSixAxisSensor }, - { 79, SetGyroscopeZeroDriftMode }, - { 100, SetSupportedNpadStyleSet }, - { 101, GetSupportedNpadStyleSet }, - { 102, SetSupportedNpadIdType }, - { 103, ActivateNpad }, - { 106, AcquireNpadStyleSetUpdateEventHandle }, - { 108, GetPlayerLedPattern }, - { 120, SetNpadJoyHoldType }, - { 121, GetNpadJoyHoldType }, - { 122, SetNpadJoyAssignmentModeSingleByDefault }, - { 123, SetNpadJoyAssignmentModeSingle }, - { 124, SetNpadJoyAssignmentModeDual }, - { 125, MergeSingleJoyAsDualJoy }, - { 128, SetNpadHandheldActivationMode }, - { 200, GetVibrationDeviceInfo }, - { 201, SendVibrationValue }, - { 203, CreateActiveVibrationDeviceList }, - { 206, SendVibrationValues } + { 0, CreateAppletResource }, + { 1, ActivateDebugPad }, + { 11, ActivateTouchScreen }, + { 21, ActivateMouse }, + { 31, ActivateKeyboard }, + { 40, AcquireXpadIdEventHandle }, + { 41, ReleaseXpadIdEventHandle }, + { 51, ActivateXpad }, + { 55, GetXpadIds }, + { 56, ActivateJoyXpad }, + { 58, GetJoyXpadLifoHandle }, + { 59, GetJoyXpadIds }, + { 60, ActivateSixAxisSensor }, + { 61, DeactivateSixAxisSensor }, + { 62, GetSixAxisSensorLifoHandle }, + { 63, ActivateJoySixAxisSensor }, + { 64, DeactivateJoySixAxisSensor }, + { 65, GetJoySixAxisSensorLifoHandle }, + { 66, StartSixAxisSensor }, + { 67, StopSixAxisSensor }, + { 68, IsSixAxisSensorFusionEnabled }, + { 69, EnableSixAxisSensorFusion }, + { 70, SetSixAxisSensorFusionParameters }, + { 71, GetSixAxisSensorFusionParameters }, + { 72, ResetSixAxisSensorFusionParameters }, + { 73, SetAccelerometerParameters }, + { 74, GetAccelerometerParameters }, + { 75, ResetAccelerometerParameters }, + { 76, SetAccelerometerPlayMode }, + { 77, GetAccelerometerPlayMode }, + { 78, ResetAccelerometerPlayMode }, + { 79, SetGyroscopeZeroDriftMode }, + { 80, GetGyroscopeZeroDriftMode }, + { 81, ResetGyroscopeZeroDriftMode }, + { 82, IsSixAxisSensorAtRest }, + { 91, ActivateGesture }, + { 100, SetSupportedNpadStyleSet }, + { 101, GetSupportedNpadStyleSet }, + { 102, SetSupportedNpadIdType }, + { 103, ActivateNpad }, + { 104, DeactivateNpad }, + { 106, AcquireNpadStyleSetUpdateEventHandle }, + { 107, DisconnectNpad }, + { 108, GetPlayerLedPattern }, + { 109, ActivateNpadWithRevision }, + { 120, SetNpadJoyHoldType }, + { 121, GetNpadJoyHoldType }, + { 122, SetNpadJoyAssignmentModeSingleByDefault }, + { 123, SetNpadJoyAssignmentModeSingle }, + { 124, SetNpadJoyAssignmentModeDual }, + { 125, MergeSingleJoyAsDualJoy }, + { 126, StartLrAssignmentMode }, + { 127, StopLrAssignmentMode }, + { 128, SetNpadHandheldActivationMode }, + { 129, GetNpadHandheldActivationMode }, + { 130, SwapNpadAssignment }, + { 131, IsUnintendedHomeButtonInputProtectionEnabled }, + { 132, EnableUnintendedHomeButtonInputProtection }, + { 133, SetNpadJoyAssignmentModeSingleWithDestination }, + { 200, GetVibrationDeviceInfo }, + { 201, SendVibrationValue }, + { 202, GetActualVibrationValue }, + { 203, CreateActiveVibrationDeviceList }, + { 204, PermitVibration }, + { 205, IsVibrationPermitted }, + { 206, SendVibrationValues }, + { 207, SendVibrationGcErmCommand }, + { 208, GetActualVibrationGcErmCommand }, + { 209, BeginPermitVibrationSession }, + { 210, EndPermitVibrationSession }, + { 300, ActivateConsoleSixAxisSensor }, + { 301, StartConsoleSixAxisSensor }, + { 302, StopConsoleSixAxisSensor }, + { 303, ActivateSevenSixAxisSensor }, + { 304, StartSevenSixAxisSensor }, + { 305, StopSevenSixAxisSensor }, + { 306, InitializeSevenSixAxisSensor }, + { 307, FinalizeSevenSixAxisSensor }, + { 308, SetSevenSixAxisSensorFusionStrength }, + { 309, GetSevenSixAxisSensorFusionStrength }, + { 400, IsUsbFullKeyControllerEnabled }, + { 401, EnableUsbFullKeyController }, + { 402, IsUsbFullKeyControllerConnected }, + { 403, HasBattery }, + { 404, HasLeftRightBattery }, + { 405, GetNpadInterfaceType }, + { 406, GetNpadLeftRightInterfaceType }, + { 500, GetPalmaConnectionHandle }, + { 501, InitializePalma }, + { 502, AcquirePalmaOperationCompleteEvent }, + { 503, GetPalmaOperationInfo }, + { 504, PlayPalmaActivity }, + { 505, SetPalmaFrModeType }, + { 506, ReadPalmaStep }, + { 507, EnablePalmaStep }, + { 508, SuspendPalmaStep }, + { 509, ResetPalmaStep }, + { 510, ReadPalmaApplicationSection }, + { 511, WritePalmaApplicationSection }, + { 512, ReadPalmaUniqueCode }, + { 513, SetPalmaUniqueCodeInvalid }, + { 1000, SetNpadCommunicationMode }, + { 1001, GetNpadCommunicationMode }, }; - NpadStyleSetUpdateEvent = new KEvent(); + NpadStyleSetUpdateEvent = new KEvent(System); + XpadIdEvent = new KEvent(System); + PalmaOperationCompleteEvent = new KEvent(System); + + NpadJoyHoldType = HidNpadJoyHoldType.Vertical; + NpadStyleTag = HidNpadStyle.FullKey | HidNpadStyle.Dual | HidNpadStyle.Left | HidNpadStyle.Right; + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual; + NpadHandheldActivationMode = HidNpadHandheldActivationMode.Dual; + GyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard; + + SensorFusionParams = new HidSensorFusionParameters(); + AccelerometerParams = new HidAccelerometerParameters(); + VibrationValue = new HidVibrationValue(); } + // CreateAppletResource(nn::applet::AppletResourceUserId) -> object public long CreateAppletResource(ServiceCtx Context) { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + MakeObject(Context, new IAppletResource(Context.Device.System.HidSharedMem)); return 0; } + // ActivateDebugPad(nn::applet::AppletResourceUserId) public long ActivateDebugPad(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } + // ActivateTouchScreen(nn::applet::AppletResourceUserId) public long ActivateTouchScreen(ServiceCtx Context) { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } + // ActivateMouse(nn::applet::AppletResourceUserId) public long ActivateMouse(ServiceCtx Context) { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } + // ActivateKeyboard(nn::applet::AppletResourceUserId) public long ActivateKeyboard(ServiceCtx Context) { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } - public long StartSixAxisSensor(ServiceCtx Context) + // AcquireXpadIdEventHandle(ulong XpadId) -> nn::sf::NativeHandle + public long AcquireXpadIdEventHandle(ServiceCtx Context) { - int Handle = Context.RequestData.ReadInt32(); + long XpadId = Context.RequestData.ReadInt64(); + if (Context.Process.HandleTable.GenerateHandle(XpadIdEvent, out XpadIdEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(XpadIdEventHandle); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. XpadId: {XpadId}"); + + return 0; + } + + // ReleaseXpadIdEventHandle(ulong XpadId) + public long ReleaseXpadIdEventHandle(ServiceCtx Context) + { + long XpadId = Context.RequestData.ReadInt64(); + + Context.Process.HandleTable.CloseHandle(XpadIdEventHandle); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. XpadId: {XpadId}"); + + return 0; + } + + // ActivateXpad(nn::hid::BasicXpadId, nn::applet::AppletResourceUserId) + public long ActivateXpad(ServiceCtx Context) + { + int BasicXpadId = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"BasicXpadId: {BasicXpadId}"); return 0; } - public long SetGyroscopeZeroDriftMode(ServiceCtx Context) + // GetXpadIds() -> long IdsCount, buffer, type: 0xa> + public long GetXpadIds(ServiceCtx Context) { - int Handle = Context.RequestData.ReadInt32(); - int Unknown = Context.RequestData.ReadInt32(); - long AppletResourceUserId = Context.RequestData.ReadInt64(); + // There is any Xpad, so we return 0 and write nothing inside the type-0xa buffer. + Context.ResponseData.Write(0L); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed."); return 0; } - public long AcquireNpadStyleSetUpdateEventHandle(ServiceCtx Context) + // ActivateJoyXpad(nn::hid::JoyXpadId) + public long ActivateJoyXpad(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(NpadStyleSetUpdateEvent); + int JoyXpadId = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // GetJoyXpadLifoHandle(nn::hid::JoyXpadId) -> nn::sf::NativeHandle + public long GetJoyXpadLifoHandle(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + int Handle = 0; Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + return 0; } - public long GetSupportedNpadStyleSet(ServiceCtx Context) + // GetJoyXpadIds() -> long IdsCount, buffer, type: 0xa> + public long GetJoyXpadIds(ServiceCtx Context) { - Context.ResponseData.Write(0); + // There is any JoyXpad, so we return 0 and write nothing inside the type-0xa buffer. + Context.ResponseData.Write(0L); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed."); return 0; } + // ActivateSixAxisSensor(nn::hid::BasicXpadId) + public long ActivateSixAxisSensor(ServiceCtx Context) + { + int BasicXpadId = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + + return 0; + } + + // DeactivateSixAxisSensor(nn::hid::BasicXpadId) + public long DeactivateSixAxisSensor(ServiceCtx Context) + { + int BasicXpadId = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + + return 0; + } + + // GetSixAxisSensorLifoHandle(nn::hid::BasicXpadId) -> nn::sf::NativeHandle + public long GetSixAxisSensorLifoHandle(ServiceCtx Context) + { + int BasicXpadId = Context.RequestData.ReadInt32(); + + int Handle = 0; + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + + return 0; + } + + // ActivateJoySixAxisSensor(nn::hid::JoyXpadId) + public long ActivateJoySixAxisSensor(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // DeactivateJoySixAxisSensor(nn::hid::JoyXpadId) + public long DeactivateJoySixAxisSensor(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // GetJoySixAxisSensorLifoHandle(nn::hid::JoyXpadId) -> nn::sf::NativeHandle + public long GetJoySixAxisSensorLifoHandle(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + int Handle = 0; + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // StartSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long StartSixAxisSensor(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle}"); + + return 0; + } + + // StopSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long StopSixAxisSensor(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle}"); + + return 0; + } + + // IsSixAxisSensorFusionEnabled(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsEnabled + public long IsSixAxisSensorFusionEnabled(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(SixAxisSensorFusionEnabled); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); + + return 0; + } + + // EnableSixAxisSensorFusion(bool Enabled, nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long EnableSixAxisSensorFusion(ServiceCtx Context) + { + SixAxisSensorFusionEnabled = Context.RequestData.ReadBoolean(); + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); + + return 0; + } + + // SetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, float RevisePower, float ReviseRange, nn::applet::AppletResourceUserId) + public long SetSixAxisSensorFusionParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + + SensorFusionParams = new HidSensorFusionParameters() + { + RevisePower = Context.RequestData.ReadInt32(), + ReviseRange = Context.RequestData.ReadInt32(), + }; + + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); + + return 0; + } + + // GetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float RevisePower, float ReviseRange) + public long GetSixAxisSensorFusionParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(SensorFusionParams.RevisePower); + Context.ResponseData.Write(SensorFusionParams.ReviseRange); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); + + return 0; + } + + // ResetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long ResetSixAxisSensorFusionParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + SensorFusionParams.RevisePower = 0; + SensorFusionParams.ReviseRange = 0; + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); + + return 0; + } + + // SetAccelerometerParameters(nn::hid::SixAxisSensorHandle, float X, float Y, nn::applet::AppletResourceUserId) + public long SetAccelerometerParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + + AccelerometerParams = new HidAccelerometerParameters() + { + X = Context.RequestData.ReadInt32(), + Y = Context.RequestData.ReadInt32(), + }; + + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); + + return 0; + } + + // GetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float X, float Y + public long GetAccelerometerParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(AccelerometerParams.X); + Context.ResponseData.Write(AccelerometerParams.Y); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); + + return 0; + } + + // ResetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long ResetAccelerometerParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + AccelerometerParams.X = 0; + AccelerometerParams.Y = 0; + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); + + return 0; + } + + // SetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, uint PlayMode, nn::applet::AppletResourceUserId) + public long SetAccelerometerPlayMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + AccelerometerPlayMode = Context.RequestData.ReadUInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); + + return 0; + } + + // GetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> uint PlayMode + public long GetAccelerometerPlayMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(AccelerometerPlayMode); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); + + return 0; + } + + // ResetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long ResetAccelerometerPlayMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + AccelerometerPlayMode = 0; + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); + + return 0; + } + + // SetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, uint GyroscopeZeroDriftMode, nn::applet::AppletResourceUserId) + public long SetGyroscopeZeroDriftMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + GyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); + + return 0; + } + + // GetGyroscopeZeroDriftMode(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> int GyroscopeZeroDriftMode + public long GetGyroscopeZeroDriftMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write((int)GyroscopeZeroDriftMode); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); + + return 0; + } + + // ResetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long ResetGyroscopeZeroDriftMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + GyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard; + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); + + return 0; + } + + // IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest + public long IsSixAxisSensorAtRest(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + bool IsAtRest = true; + + Context.ResponseData.Write(IsAtRest); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"IsAtRest: {IsAtRest}"); + + return 0; + } + + // ActivateGesture(nn::applet::AppletResourceUserId, int Unknown0) + public long ActivateGesture(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int Unknown0 = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0}"); + + return 0; + } + + + // SetSupportedNpadStyleSet(nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag) public long SetSupportedNpadStyleSet(ServiceCtx Context) { - long Unknown0 = Context.RequestData.ReadInt64(); - long Unknown8 = Context.RequestData.ReadInt64(); + NpadStyleTag = (HidNpadStyle)Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadStyleTag: {NpadStyleTag}"); return 0; } + // GetSupportedNpadStyleSet(nn::applet::AppletResourceUserId) -> uint nn::hid::NpadStyleTag + public long GetSupportedNpadStyleSet(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write((int)NpadStyleTag); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadStyleTag: {NpadStyleTag}"); + + return 0; + } + + // SetSupportedNpadIdType(nn::applet::AppletResourceUserId, array) public long SetSupportedNpadIdType(ServiceCtx Context) { - long Unknown = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + HidControllerId NpadIdType = (HidControllerId)Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadIdType: {NpadIdType}"); return 0; } + // ActivateNpad(nn::applet::AppletResourceUserId) public long ActivateNpad(ServiceCtx Context) { - long Unknown = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } + // DeactivateNpad(nn::applet::AppletResourceUserId) + public long DeactivateNpad(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // AcquireNpadStyleSetUpdateEventHandle(nn::applet::AppletResourceUserId, uint, ulong) -> nn::sf::NativeHandle + public long AcquireNpadStyleSetUpdateEventHandle(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int NpadId = Context.RequestData.ReadInt32(); + long NpadStyleSet = Context.RequestData.ReadInt64(); + + if (Context.Process.HandleTable.GenerateHandle(NpadStyleSetUpdateEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadId: {NpadId} - " + + $"NpadStyleSet: {NpadStyleSet}"); + + return 0; + } + + // DisconnectNpad(nn::applet::AppletResourceUserId, uint NpadIdType) + public long DisconnectNpad(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int NpadIdType = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadIdType: {NpadIdType}"); + + return 0; + } + + // GetPlayerLedPattern(uint NpadId) -> ulong LedPattern public long GetPlayerLedPattern(ServiceCtx Context) { - long Unknown = Context.RequestData.ReadInt32(); + int NpadId = Context.RequestData.ReadInt32(); - Context.ResponseData.Write(0L); + long LedPattern = 0; - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.ResponseData.Write(LedPattern); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - Pattern: {LedPattern}"); return 0; } + // ActivateNpadWithRevision(nn::applet::AppletResourceUserId, int Unknown) + public long ActivateNpadWithRevision(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int Unknown = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - Unknown: {Unknown}"); + + return 0; + } + + // SetNpadJoyHoldType(nn::applet::AppletResourceUserId, long NpadJoyHoldType) public long SetNpadJoyHoldType(ServiceCtx Context) { - long Unknown0 = Context.RequestData.ReadInt64(); - long Unknown8 = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + NpadJoyHoldType = (HidNpadJoyHoldType)Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadJoyHoldType: {NpadJoyHoldType}"); return 0; } + // GetNpadJoyHoldType(nn::applet::AppletResourceUserId) -> long NpadJoyHoldType public long GetNpadJoyHoldType(ServiceCtx Context) { - Context.ResponseData.Write(0L); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.ResponseData.Write((long)NpadJoyHoldType); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadJoyHoldTypeValue: {NpadJoyHoldType}"); return 0; } + // SetNpadJoyAssignmentModeSingleByDefault(uint HidControllerId, nn::applet::AppletResourceUserId) public long SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx Context) { - HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - long AppletUserResourceId = Context.RequestData.ReadInt64(); + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } + // SetNpadJoyAssignmentModeSingle(uint HidControllerId, nn::applet::AppletResourceUserId, long HidNpadJoyDeviceType) public long SetNpadJoyAssignmentModeSingle(ServiceCtx Context) { - HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + HidNpadJoyDeviceType HidNpadJoyDeviceType = (HidNpadJoyDeviceType)Context.RequestData.ReadInt64(); - long AppletUserResourceId = Context.RequestData.ReadInt64(); - long NpadJoyDeviceType = Context.RequestData.ReadInt64(); + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"HidNpadJoyDeviceType: {HidNpadJoyDeviceType} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } + // SetNpadJoyAssignmentModeDual(uint HidControllerId, nn::applet::AppletResourceUserId) public long SetNpadJoyAssignmentModeDual(ServiceCtx Context) { - HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - long AppletUserResourceId = Context.RequestData.ReadInt64(); + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual; - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } + // MergeSingleJoyAsDualJoy(uint SingleJoyId0, uint SingleJoyId1, nn::applet::AppletResourceUserId) public long MergeSingleJoyAsDualJoy(ServiceCtx Context) { - long Unknown0 = Context.RequestData.ReadInt32(); - long Unknown8 = Context.RequestData.ReadInt32(); - long AppletUserResourceId = Context.RequestData.ReadInt64(); + long SingleJoyId0 = Context.RequestData.ReadInt32(); + long SingleJoyId1 = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SingleJoyId0: {SingleJoyId0} - " + + $"SingleJoyId1: {SingleJoyId1}"); return 0; } + // StartLrAssignmentMode(nn::applet::AppletResourceUserId) + public long StartLrAssignmentMode(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // StopLrAssignmentMode(nn::applet::AppletResourceUserId) + public long StopLrAssignmentMode(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // SetNpadHandheldActivationMode(nn::applet::AppletResourceUserId, long HidNpadHandheldActivationMode) public long SetNpadHandheldActivationMode(ServiceCtx Context) { - long AppletUserResourceId = Context.RequestData.ReadInt64(); - long Unknown = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + NpadHandheldActivationMode = (HidNpadHandheldActivationMode)Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadHandheldActivationMode: {NpadHandheldActivationMode}"); return 0; } + // GetNpadHandheldActivationMode(nn::applet::AppletResourceUserId) -> long HidNpadHandheldActivationMode + public long GetNpadHandheldActivationMode(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write((long)NpadHandheldActivationMode); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadHandheldActivationMode: {NpadHandheldActivationMode}"); + + return 0; + } + + // SwapNpadAssignment(uint OldNpadAssignment, uint NewNpadAssignment, nn::applet::AppletResourceUserId) + public long SwapNpadAssignment(ServiceCtx Context) + { + int OldNpadAssignment = Context.RequestData.ReadInt32(); + int NewNpadAssignment = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"OldNpadAssignment: {OldNpadAssignment} - " + + $"NewNpadAssignment: {NewNpadAssignment}"); + + return 0; + } + + // IsUnintendedHomeButtonInputProtectionEnabled(uint Unknown0, nn::applet::AppletResourceUserId) -> bool IsEnabled + public long IsUnintendedHomeButtonInputProtectionEnabled(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(UnintendedHomeButtonInputProtectionEnabled); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"UnintendedHomeButtonInputProtectionEnabled: {UnintendedHomeButtonInputProtectionEnabled}"); + + return 0; + } + + // EnableUnintendedHomeButtonInputProtection(bool Enable, uint Unknown0, nn::applet::AppletResourceUserId) + public long EnableUnintendedHomeButtonInputProtection(ServiceCtx Context) + { + UnintendedHomeButtonInputProtectionEnabled = Context.RequestData.ReadBoolean(); + uint Unknown0 = Context.RequestData.ReadUInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"UnintendedHomeButtonInputProtectionEnable: {UnintendedHomeButtonInputProtectionEnabled}"); + + return 0; + } + + // SetNpadJoyAssignmentModeSingleWithDestination(uint HidControllerId, long HidNpadJoyDeviceType, nn::applet::AppletResourceUserId) -> bool Unknown0, uint Unknown1 + public long SetNpadJoyAssignmentModeSingleWithDestination(ServiceCtx Context) + { + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + HidNpadJoyDeviceType HidNpadJoyDeviceType = (HidNpadJoyDeviceType)Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; + + Context.ResponseData.Write(0); //Unknown0 + Context.ResponseData.Write(0); //Unknown1 + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"HidNpadJoyDeviceType: {HidNpadJoyDeviceType} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode} - " + + $"Unknown0: 0 - " + + $"Unknown1: 0"); + + return 0; + } + + // GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo public long GetVibrationDeviceInfo(ServiceCtx Context) { int VibrationDeviceHandle = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + HidVibrationDeviceValue DeviceInfo = new HidVibrationDeviceValue + { + DeviceType = HidVibrationDeviceType.None, + Position = HidVibrationDevicePosition.None + }; - Context.ResponseData.Write(0L); //VibrationDeviceInfoForIpc + Context.ResponseData.Write((int)DeviceInfo.DeviceType); + Context.ResponseData.Write((int)DeviceInfo.Position); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"DeviceType: {DeviceInfo.DeviceType} - " + + $"Position: {DeviceInfo.Position}"); return 0; } + // SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId) public long SendVibrationValue(ServiceCtx Context) { int VibrationDeviceHandle = Context.RequestData.ReadInt32(); - int VibrationValue1 = Context.RequestData.ReadInt32(); - int VibrationValue2 = Context.RequestData.ReadInt32(); - int VibrationValue3 = Context.RequestData.ReadInt32(); - int VibrationValue4 = Context.RequestData.ReadInt32(); + VibrationValue = new HidVibrationValue + { + AmplitudeLow = Context.RequestData.ReadSingle(), + FrequencyLow = Context.RequestData.ReadSingle(), + AmplitudeHigh = Context.RequestData.ReadSingle(), + FrequencyHigh = Context.RequestData.ReadSingle() + }; - long AppletUserResourceId = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"AmplitudeLow: {VibrationValue.AmplitudeLow} - " + + $"FrequencyLow: {VibrationValue.FrequencyLow} - " + + $"AmplitudeHigh: {VibrationValue.AmplitudeHigh} - " + + $"FrequencyHigh: {VibrationValue.FrequencyHigh}"); return 0; } + // GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue + public long GetActualVibrationValue(ServiceCtx Context) + { + int VibrationDeviceHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(VibrationValue.AmplitudeLow); + Context.ResponseData.Write(VibrationValue.FrequencyLow); + Context.ResponseData.Write(VibrationValue.AmplitudeHigh); + Context.ResponseData.Write(VibrationValue.FrequencyHigh); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"AmplitudeLow: {VibrationValue.AmplitudeLow} - " + + $"FrequencyLow: {VibrationValue.FrequencyLow} - " + + $"AmplitudeHigh: {VibrationValue.AmplitudeHigh} - " + + $"FrequencyHigh: {VibrationValue.FrequencyHigh}"); + + return 0; + } + + // CreateActiveVibrationDeviceList() -> object public long CreateActiveVibrationDeviceList(ServiceCtx Context) { MakeObject(Context, new IActiveApplicationDeviceList()); @@ -276,24 +1036,488 @@ namespace Ryujinx.HLE.HOS.Services.Hid return 0; } - public long SendVibrationValues(ServiceCtx Context) + // PermitVibration(bool Enable) + public long PermitVibration(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + VibrationPermitted = Context.RequestData.ReadBoolean(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationPermitted: {VibrationPermitted}"); return 0; } - public void Dispose() + // IsVibrationPermitted() -> bool IsEnabled + public long IsVibrationPermitted(ServiceCtx Context) { - Dispose(true); + Context.ResponseData.Write(VibrationPermitted); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationPermitted: {VibrationPermitted}"); + + return 0; } - protected virtual void Dispose(bool Disposing) + // SendVibrationValues(nn::applet::AppletResourceUserId, buffer, type: 9>, buffer, type: 9>) + public long SendVibrationValues(ServiceCtx Context) { - if (Disposing) + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + byte[] VibrationDeviceHandleBuffer = Context.Memory.ReadBytes( + Context.Request.PtrBuff[0].Position, + Context.Request.PtrBuff[0].Size); + + byte[] VibrationValueBuffer = Context.Memory.ReadBytes( + Context.Request.PtrBuff[1].Position, + Context.Request.PtrBuff[1].Size); + + //Todo: Read all handles and values from buffer. + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandleBufferLength: {VibrationDeviceHandleBuffer.Length} - " + + $"VibrationValueBufferLength: {VibrationValueBuffer.Length}"); + + return 0; + } + + // SendVibrationGcErmCommand(nn::hid::VibrationDeviceHandle, nn::hid::VibrationGcErmCommand, nn::applet::AppletResourceUserId) + public long SendVibrationGcErmCommand(ServiceCtx Context) + { + int VibrationDeviceHandle = Context.RequestData.ReadInt32(); + long VibrationGcErmCommand = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"VibrationGcErmCommand: {VibrationGcErmCommand}"); + + return 0; + } + + // GetActualVibrationGcErmCommand(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationGcErmCommand + public long GetActualVibrationGcErmCommand(ServiceCtx Context) + { + int VibrationDeviceHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(VibrationGcErmCommand); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"VibrationGcErmCommand: {VibrationGcErmCommand}"); + + return 0; + } + + // BeginPermitVibrationSession(nn::applet::AppletResourceUserId) + public long BeginPermitVibrationSession(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // EndPermitVibrationSession() + public long EndPermitVibrationSession(ServiceCtx Context) + { + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed."); + + return 0; + } + + // ActivateConsoleSixAxisSensor(nn::applet::AppletResourceUserId) + public long ActivateConsoleSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // StartConsoleSixAxisSensor(nn::hid::ConsoleSixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long StartConsoleSixAxisSensor(ServiceCtx Context) + { + int ConsoleSixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); + + return 0; + } + + // StopConsoleSixAxisSensor(nn::hid::ConsoleSixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long StopConsoleSixAxisSensor(ServiceCtx Context) + { + int ConsoleSixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); + + return 0; + } + + // ActivateSevenSixAxisSensor(nn::applet::AppletResourceUserId) + public long ActivateSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // StartSevenSixAxisSensor(nn::applet::AppletResourceUserId) + public long StartSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // StopSevenSixAxisSensor(nn::applet::AppletResourceUserId) + public long StopSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // InitializeSevenSixAxisSensor(array, ulong Counter0, array, ulong Counter1, nn::applet::AppletResourceUserId) + public long InitializeSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + long Counter0 = Context.RequestData.ReadInt64(); + long Counter1 = Context.RequestData.ReadInt64(); + + // Todo: Determine if array is a buffer or not... + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Counter0: {Counter0} - " + + $"Counter1: {Counter1}"); + + return 0; + } + + // FinalizeSevenSixAxisSensor(nn::applet::AppletResourceUserId) + public long FinalizeSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // SetSevenSixAxisSensorFusionStrength(float Strength, nn::applet::AppletResourceUserId) + public long SetSevenSixAxisSensorFusionStrength(ServiceCtx Context) + { + SevenSixAxisSensorFusionStrength = Context.RequestData.ReadSingle(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SevenSixAxisSensorFusionStrength: {SevenSixAxisSensorFusionStrength}"); + + return 0; + } + + // GetSevenSixAxisSensorFusionStrength(nn::applet::AppletResourceUserId) -> float Strength + public long GetSevenSixAxisSensorFusionStrength(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(SevenSixAxisSensorFusionStrength); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SevenSixAxisSensorFusionStrength: {SevenSixAxisSensorFusionStrength}"); + + return 0; + } + + // IsUsbFullKeyControllerEnabled() -> bool IsEnabled + public long IsUsbFullKeyControllerEnabled(ServiceCtx Context) + { + Context.ResponseData.Write(UsbFullKeyControllerEnabled); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. UsbFullKeyControllerEnabled: {UsbFullKeyControllerEnabled}"); + + return 0; + } + + // EnableUsbFullKeyController(bool Enable) + public long EnableUsbFullKeyController(ServiceCtx Context) + { + UsbFullKeyControllerEnabled = Context.RequestData.ReadBoolean(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. UsbFullKeyControllerEnabled: {UsbFullKeyControllerEnabled}"); + + return 0; + } + + // IsUsbFullKeyControllerConnected(uint Unknown0) -> bool Connected + public long IsUsbFullKeyControllerConnected(ServiceCtx Context) + { + int Unknown0 = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(true); //FullKeyController is always connected ? + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. Unknown0: {Unknown0} - Connected: true"); + + return 0; + } + + // HasBattery(uint NpadId) -> bool HasBattery + public long HasBattery(ServiceCtx Context) + { + int NpadId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(true); //Npad always got a battery ? + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasBattery: true"); + + return 0; + } + + // HasLeftRightBattery(uint NpadId) -> bool HasLeftBattery, bool HasRightBattery + public long HasLeftRightBattery(ServiceCtx Context) + { + int NpadId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(true); //Npad always got a left battery ? + Context.ResponseData.Write(true); //Npad always got a right battery ? + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasLeftBattery: true - HasRightBattery: true"); + + return 0; + } + + // GetNpadInterfaceType(uint NpadId) -> uchar InterfaceType + public long GetNpadInterfaceType(ServiceCtx Context) + { + int NpadId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write((byte)0); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - NpadInterfaceType: 0"); + + return 0; + } + + // GetNpadLeftRightInterfaceType(uint NpadId) -> uchar LeftInterfaceType, uchar RightInterfaceType + public long GetNpadLeftRightInterfaceType(ServiceCtx Context) + { + int NpadId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write((byte)0); + Context.ResponseData.Write((byte)0); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - " + + $"LeftInterfaceType: 0 - " + + $"RightInterfaceType: 0"); + + return 0; + } + + // GetPalmaConnectionHandle(uint Unknown0, nn::applet::AppletResourceUserId) -> nn::hid::PalmaConnectionHandle + public long GetPalmaConnectionHandle(ServiceCtx Context) + { + int Unknown0 = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + int PalmaConnectionHandle = 0; + + Context.ResponseData.Write(PalmaConnectionHandle); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // InitializePalma(nn::hid::PalmaConnectionHandle) + public long InitializePalma(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // AcquirePalmaOperationCompleteEvent(nn::hid::PalmaConnectionHandle) -> nn::sf::NativeHandle + public long AcquirePalmaOperationCompleteEvent(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + if (Context.Process.HandleTable.GenerateHandle(PalmaOperationCompleteEvent, out int Handle) != KernelResult.Success) { - NpadStyleSetUpdateEvent.Dispose(); + throw new InvalidOperationException("Out of handles!"); } + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // GetPalmaOperationInfo(nn::hid::PalmaConnectionHandle) -> long Unknown0, buffer + public long GetPalmaOperationInfo(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + long Unknown0 = 0; //Counter? + + Context.ResponseData.Write(Unknown0); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0}"); + + return 0; + } + + // PlayPalmaActivity(nn::hid::PalmaConnectionHandle, ulong Unknown0) + public long PlayPalmaActivity(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + long Unknown0 = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0}"); + + return 0; + } + + // SetPalmaFrModeType(nn::hid::PalmaConnectionHandle, ulong FrModeType) + public long SetPalmaFrModeType(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + long FrModeType = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"FrModeType: {FrModeType}"); + + return 0; + } + + // ReadPalmaStep(nn::hid::PalmaConnectionHandle) + public long ReadPalmaStep(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // EnablePalmaStep(nn::hid::PalmaConnectionHandle, bool Enable) + public long EnablePalmaStep(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + bool EnabledPalmaStep = Context.RequestData.ReadBoolean(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"EnabledPalmaStep: {EnabledPalmaStep}"); + + return 0; + } + + // SuspendPalmaStep(nn::hid::PalmaConnectionHandle) + public long SuspendPalmaStep(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // ResetPalmaStep(nn::hid::PalmaConnectionHandle) + public long ResetPalmaStep(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // ReadPalmaApplicationSection(nn::hid::PalmaConnectionHandle, ulong Unknown0, ulong Unknown1) + public long ReadPalmaApplicationSection(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + long Unknown0 = Context.RequestData.ReadInt64(); + long Unknown1 = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); + + return 0; + } + + // WritePalmaApplicationSection(nn::hid::PalmaConnectionHandle, ulong Unknown0, ulong Unknown1, nn::hid::PalmaApplicationSectionAccessBuffer) + public long WritePalmaApplicationSection(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + long Unknown0 = Context.RequestData.ReadInt64(); + long Unknown1 = Context.RequestData.ReadInt64(); + // nn::hid::PalmaApplicationSectionAccessBuffer cast is unknown + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); + + return 0; + } + + // ReadPalmaUniqueCode(nn::hid::PalmaConnectionHandle) + public long ReadPalmaUniqueCode(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // SetPalmaUniqueCodeInvalid(nn::hid::PalmaConnectionHandle) + public long SetPalmaUniqueCodeInvalid(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // SetNpadCommunicationMode(long CommunicationMode, nn::applet::AppletResourceUserId) + public long SetNpadCommunicationMode(ServiceCtx Context) + { + NpadCommunicationMode = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadCommunicationMode: {NpadCommunicationMode}"); + + return 0; + } + + // GetNpadCommunicationMode() -> long CommunicationMode + public long GetNpadCommunicationMode(ServiceCtx Context) + { + Context.ResponseData.Write(NpadCommunicationMode); + + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. CommunicationMode: {NpadCommunicationMode}"); + + return 0; } } } diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index 8e487d55c1..60a4431e9e 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.IO; @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services { Context.ResponseData.BaseStream.Seek(IsDomain ? 0x20 : 0x10, SeekOrigin.Begin); - Context.Device.Log.PrintDebug(LogClass.KernelIpc, $"{Service.GetType().Name}: {ProcessRequest.Method.Name}"); + Logger.PrintDebug(LogClass.KernelIpc, $"{Service.GetType().Name}: {ProcessRequest.Method.Name}"); long Result = ProcessRequest(Context); @@ -132,7 +132,10 @@ namespace Ryujinx.HLE.HOS.Services { KSession Session = new KSession(Obj, Context.Session.ServiceName); - int Handle = Context.Process.HandleTable.OpenHandle(Session); + if (Context.Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); } @@ -146,7 +149,7 @@ namespace Ryujinx.HLE.HOS.Services { int Handle = Context.Request.HandleDesc.ToMove[Index]; - KSession Session = Context.Process.HandleTable.GetData(Handle); + KSession Session = Context.Process.HandleTable.GetObject(Handle); return Session?.Service is T ? (T)Session.Service : null; } diff --git a/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs new file mode 100644 index 0000000000..155faea3e6 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs @@ -0,0 +1,44 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Irs +{ + class IIrSensorServer : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private bool Activated; + + public IIrSensorServer() + { + m_Commands = new Dictionary() + { + { 302, ActivateIrsensor }, + { 303, DeactivateIrsensor } + }; + } + + // ActivateIrsensor(nn::applet::AppletResourceUserId, pid) + public long ActivateIrsensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceIrs, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // DeactivateIrsensor(nn::applet::AppletResourceUserId, pid) + public long DeactivateIrsensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceIrs, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs new file mode 100644 index 0000000000..4d595fde27 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs @@ -0,0 +1,457 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +using static Ryujinx.HLE.HOS.ErrorCode; + +namespace Ryujinx.HLE.HOS.Services.Ldr +{ + [StructLayout(LayoutKind.Explicit, Size = 0x350)] + unsafe struct NrrHeader + { + [FieldOffset(0)] + public uint Magic; + + [FieldOffset(0x10)] + public ulong TitleIdMask; + + [FieldOffset(0x18)] + public ulong TitleIdPattern; + + [FieldOffset(0x30)] + public fixed byte Modulus[0x100]; + + [FieldOffset(0x130)] + public fixed byte FixedKeySignature[0x100]; + + [FieldOffset(0x230)] + public fixed byte NrrSignature[0x100]; + + [FieldOffset(0x330)] + public ulong TitleIdMin; + + [FieldOffset(0x338)] + public uint NrrSize; + + [FieldOffset(0x340)] + public uint HashOffset; + + [FieldOffset(0x344)] + public uint HashCount; + } + + class NrrInfo + { + public NrrHeader Header { get; private set; } + public List Hashes { get; private set; } + public long NrrAddress { get; private set; } + + public NrrInfo(long NrrAddress, NrrHeader Header, List Hashes) + { + this.NrrAddress = NrrAddress; + this.Header = Header; + this.Hashes = Hashes; + } + } + + class NroInfo + { + public Nro Executable { get; private set; } + public byte[] Hash { get; private set; } + public long NroAddress { get; private set; } + public long TotalSize { get; private set; } + public long NroMappedAddress { get; set; } + + public NroInfo(Nro Executable, byte[] Hash, long TotalSize) + { + this.Executable = Executable; + this.Hash = Hash; + this.TotalSize = TotalSize; + } + } + + class IRoInterface : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private const int MaxNrr = 0x40; + private const int MaxNro = 0x40; + + private const uint NrrMagic = 0x3052524E; + private const uint NroMagic = 0x304F524E; + + private List NrrInfos; + private List NroInfos; + + private bool IsInitialized; + + public IRoInterface() + { + m_Commands = new Dictionary() + { + { 0, LoadNro }, + { 1, UnloadNro }, + { 2, LoadNrr }, + { 3, UnloadNrr }, + { 4, Initialize }, + }; + + NrrInfos = new List(MaxNrr); + NroInfos = new List(MaxNro); + } + + private long ParseNrr(out NrrInfo NrrInfo, ServiceCtx Context, long NrrAddress, long NrrSize) + { + NrrInfo = null; + + if (NrrSize == 0 || NrrAddress + NrrSize <= NrrAddress || (NrrSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if ((NrrAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + StructReader Reader = new StructReader(Context.Memory, NrrAddress); + NrrHeader Header = Reader.Read(); + + if (Header.Magic != NrrMagic) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNrr); + } + else if (Header.NrrSize != NrrSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + + List Hashes = new List(); + + for (int i = 0; i < Header.HashCount; i++) + { + Hashes.Add(Context.Memory.ReadBytes(NrrAddress + Header.HashOffset + (i * 0x20), 0x20)); + } + + NrrInfo = new NrrInfo(NrrAddress, Header, Hashes); + + return 0; + } + + public bool IsNroHashPresent(byte[] NroHash) + { + foreach (NrrInfo Info in NrrInfos) + { + foreach (byte[] Hash in Info.Hashes) + { + if (Hash.SequenceEqual(NroHash)) + { + return true; + } + } + } + + return false; + } + + public bool IsNroLoaded(byte[] NroHash) + { + foreach (NroInfo Info in NroInfos) + { + if (Info.Hash.SequenceEqual(NroHash)) + { + return true; + } + } + + return false; + } + + public long ParseNro(out NroInfo Res, ServiceCtx Context, long NroHeapAddress, long NroSize, long BssHeapAddress, long BssSize) + { + Res = null; + + if (NroInfos.Count >= MaxNro) + { + return MakeError(ErrorModule.Loader, LoaderErr.MaxNro); + } + else if (NroSize == 0 || NroHeapAddress + NroSize <= NroHeapAddress || (NroSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if (BssSize != 0 && (BssHeapAddress + BssSize) <= BssHeapAddress) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if ((NroHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + uint Magic = Context.Memory.ReadUInt32(NroHeapAddress + 0x10); + uint NroFileSize = Context.Memory.ReadUInt32(NroHeapAddress + 0x18); + + if (Magic != NroMagic || NroSize != NroFileSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + byte[] NroData = Context.Memory.ReadBytes(NroHeapAddress, NroSize); + byte[] NroHash = null; + + MemoryStream Stream = new MemoryStream(NroData); + + using (SHA256 Hasher = SHA256.Create()) + { + NroHash = Hasher.ComputeHash(Stream); + } + + if (!IsNroHashPresent(NroHash)) + { + return MakeError(ErrorModule.Loader, LoaderErr.NroHashNotPresent); + } + + if (IsNroLoaded(NroHash)) + { + return MakeError(ErrorModule.Loader, LoaderErr.NroAlreadyLoaded); + } + + Stream.Position = 0; + + Nro Executable = new Nro(Stream, "memory", NroHeapAddress, BssHeapAddress); + + // check if everything is page align. + if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 + || (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + // check if everything is contiguous. + if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length + || Executable.DataOffset != Executable.ROOffset + Executable.RO.Length + || NroFileSize != Executable.DataOffset + Executable.Data.Length) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + // finally check the bss size match. + if (Executable.BssSize != BssSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + Res = new NroInfo(Executable, NroHash, Executable.Text.Length + Executable.RO.Length + Executable.Data.Length + Executable.BssSize); + + return 0; + } + + private long MapNro(ServiceCtx Context, NroInfo Info, out long NroMappedAddress) + { + NroMappedAddress = 0; + long TargetAddress = Context.Process.MemoryManager.AddrSpaceStart; + + long HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart; + long HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd; + + long MapRegionStart = Context.Process.MemoryManager.MapRegionStart; + long MapRegionEnd = Context.Process.MemoryManager.MapRegionEnd; + + while (true) + { + if (TargetAddress + Info.TotalSize >= Context.Process.MemoryManager.AddrSpaceEnd) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState); + } + + bool IsValidAddress = !(HeapRegionStart > 0 && HeapRegionStart <= TargetAddress + Info.TotalSize - 1 + && TargetAddress <= HeapRegionEnd - 1) + && !(MapRegionStart > 0 + && MapRegionStart <= TargetAddress + Info.TotalSize - 1 + && TargetAddress <= MapRegionEnd - 1); + + if (IsValidAddress && Context.Process.MemoryManager.HleIsUnmapped(TargetAddress, Info.TotalSize)) + { + break; + } + + TargetAddress += 0x1000; + } + + Context.Process.LoadProgram(Info.Executable, TargetAddress); + + Info.NroMappedAddress = TargetAddress; + NroMappedAddress = TargetAddress; + + return 0; + } + + private long RemoveNrrInfo(long NrrAddress) + { + foreach (NrrInfo Info in NrrInfos) + { + if (Info.NrrAddress == NrrAddress) + { + NrrInfos.Remove(Info); + + return 0; + } + } + + return MakeError(ErrorModule.Loader, LoaderErr.BadNrrAddress); + } + + private long RemoveNroInfo(ServiceCtx Context, long NroMappedAddress, long NroHeapAddress) + { + foreach (NroInfo Info in NroInfos) + { + if (Info.NroMappedAddress == NroMappedAddress && Info.Executable.SourceAddress == NroHeapAddress) + { + NroInfos.Remove(Info); + + Context.Process.RemoveProgram(Info.NroMappedAddress); + + long Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize); + + if (Result == 0 && Info.Executable.BssSize != 0) + { + Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress + Info.TotalSize - Info.Executable.BssSize, Info.Executable.BssAddress, Info.Executable.BssSize); + } + + return Result; + } + } + + return MakeError(ErrorModule.Loader, LoaderErr.BadNroAddress); + } + + // LoadNro(u64, u64, u64, u64, u64, pid) -> u64 + public long LoadNro(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NroHeapAddress = Context.RequestData.ReadInt64(); + long NroSize = Context.RequestData.ReadInt64(); + long BssHeapAddress = Context.RequestData.ReadInt64(); + long BssSize = Context.RequestData.ReadInt64(); + + long NroMappedAddress = 0; + + if (IsInitialized) + { + NroInfo Info; + + Result = ParseNro(out Info, Context, NroHeapAddress, NroSize, BssHeapAddress, BssSize); + + if (Result == 0) + { + Result = MapNro(Context, Info, out NroMappedAddress); + + if (Result == 0) + { + NroInfos.Add(Info); + } + } + } + + Context.ResponseData.Write(NroMappedAddress); + + return Result; + } + + // UnloadNro(u64, u64, pid) + public long UnloadNro(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + long NroMappedAddress = Context.RequestData.ReadInt64(); + long NroHeapAddress = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + if ((NroMappedAddress & 0xFFF) != 0 || (NroHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + Result = RemoveNroInfo(Context, NroMappedAddress, NroHeapAddress); + } + + return Result; + } + + // LoadNrr(u64, u64, u64, pid) + public long LoadNrr(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NrrAddress = Context.RequestData.ReadInt64(); + long NrrSize = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + NrrInfo Info; + Result = ParseNrr(out Info, Context, NrrAddress, NrrSize); + + if(Result == 0) + { + if (NrrInfos.Count >= MaxNrr) + { + Result = MakeError(ErrorModule.Loader, LoaderErr.MaxNrr); + } + else + { + NrrInfos.Add(Info); + } + } + } + + return Result; + } + + // UnloadNrr(u64, u64, pid) + public long UnloadNrr(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NrrHeapAddress = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + if ((NrrHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + Result = RemoveNrrInfo(NrrHeapAddress); + } + + return Result; + } + + // Initialize(u64, pid, KObject) + public long Initialize(ServiceCtx Context) + { + // TODO: we actually ignore the pid and process handle receive, we will need to use them when we will have multi process support. + IsInitialized = true; + + return 0; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs b/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs new file mode 100644 index 0000000000..ba77a5cc13 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.HLE.HOS.Services.Ldr +{ + static class LoaderErr + { + public const int InvalidMemoryState = 51; + public const int InvalidNro = 52; + public const int InvalidNrr = 53; + public const int MaxNro = 55; + public const int MaxNrr = 56; + public const int NroAlreadyLoaded = 57; + public const int NroHashNotPresent = 54; + public const int UnalignedAddress = 81; + public const int BadSize = 82; + public const int BadNroAddress = 84; + public const int BadNrrAddress = 85; + public const int BadInitialization = 87; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Lm/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/ILogger.cs index 5d99970990..8a630ce515 100644 --- a/Ryujinx.HLE/HOS/Services/Lm/ILogger.cs +++ b/Ryujinx.HLE/HOS/Services/Lm/ILogger.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; using System.IO; using System.Text; @@ -88,11 +88,11 @@ namespace Ryujinx.HLE.HOS.Services.Lm switch((LmLogLevel)Level) { - case LmLogLevel.Trace: Context.Device.Log.PrintDebug (LogClass.ServiceLm, Text); break; - case LmLogLevel.Info: Context.Device.Log.PrintInfo (LogClass.ServiceLm, Text); break; - case LmLogLevel.Warning: Context.Device.Log.PrintWarning(LogClass.ServiceLm, Text); break; - case LmLogLevel.Error: Context.Device.Log.PrintError (LogClass.ServiceLm, Text); break; - case LmLogLevel.Critical: Context.Device.Log.PrintError (LogClass.ServiceLm, Text); break; + case LmLogLevel.Trace: Logger.PrintDebug (LogClass.ServiceLm, Text); break; + case LmLogLevel.Info: Logger.PrintInfo (LogClass.ServiceLm, Text); break; + case LmLogLevel.Warning: Logger.PrintWarning(LogClass.ServiceLm, Text); break; + case LmLogLevel.Error: Logger.PrintError (LogClass.ServiceLm, Text); break; + case LmLogLevel.Critical: Logger.PrintError (LogClass.ServiceLm, Text); break; } } diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs index e65ef08679..88cd57cff7 100644 --- a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Mm @@ -14,22 +14,35 @@ namespace Ryujinx.HLE.HOS.Services.Mm { m_Commands = new Dictionary() { - { 4, Initialize }, - { 6, SetAndWait }, - { 7, Get } + { 1, InitializeOld }, + { 4, Initialize }, + { 6, SetAndWait }, + { 7, Get } }; } + // InitializeOld(u32, u32, u32) + public long InitializeOld(ServiceCtx Context) + { + int Unknown0 = Context.RequestData.ReadInt32(); + int Unknown1 = Context.RequestData.ReadInt32(); + int Unknown2 = Context.RequestData.ReadInt32(); + + Logger.PrintStub(LogClass.ServiceMm, "Stubbed."); + + return 0; + } + public long Initialize(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceMm, "Stubbed."); return 0; } public long SetAndWait(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceMm, "Stubbed."); return 0; } @@ -38,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm { Context.ResponseData.Write(0); - Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceMm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs index eac90da421..f0dc825b08 100644 --- a/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs +++ b/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs @@ -1,7 +1,8 @@ -using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Input; -using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Nfp @@ -24,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp private KEvent AvailabilityChangeEvent; - public IUser() + public IUser(Horizon System) { m_Commands = new Dictionary() { @@ -37,14 +38,14 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { 23, AttachAvailabilityChangeEvent } }; - ActivateEvent = new KEvent(); - DeactivateEvent = new KEvent(); - AvailabilityChangeEvent = new KEvent(); + ActivateEvent = new KEvent(System); + DeactivateEvent = new KEvent(System); + AvailabilityChangeEvent = new KEvent(System); } public long Initialize(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); State = State.Initialized; @@ -53,9 +54,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfp public long AttachActivateEvent(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); - int Handle = Context.Process.HandleTable.OpenHandle(ActivateEvent); + if (Context.Process.HandleTable.GenerateHandle(ActivateEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);; @@ -64,9 +68,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfp public long AttachDeactivateEvent(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); - int Handle = Context.Process.HandleTable.OpenHandle(DeactivateEvent); + if (Context.Process.HandleTable.GenerateHandle(DeactivateEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -77,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.ResponseData.Write((int)State); - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); return 0; } @@ -86,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.ResponseData.Write((int)DeviceState); - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); return 0; } @@ -95,16 +102,19 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.ResponseData.Write((int)NpadId); - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); return 0; } public long AttachAvailabilityChangeEvent(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); - int Handle = Context.Process.HandleTable.OpenHandle(AvailabilityChangeEvent); + if (Context.Process.HandleTable.GenerateHandle(AvailabilityChangeEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Nfp/IUserManager.cs b/Ryujinx.HLE/HOS/Services/Nfp/IUserManager.cs index 770f0341ec..e5d5a4f1cb 100644 --- a/Ryujinx.HLE/HOS/Services/Nfp/IUserManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfp/IUserManager.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp public long GetUserInterface(ServiceCtx Context) { - MakeObject(Context, new IUser()); + MakeObject(Context, new IUser(Context.Device.System)); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/IGeneralService.cs index ec68247ba9..bc23ea9100 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/IGeneralService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.Linq; @@ -30,9 +30,9 @@ namespace Ryujinx.HLE.HOS.Services.Nifm { int Unknown = Context.RequestData.ReadInt32(); - MakeObject(Context, new IRequest()); + MakeObject(Context, new IRequest(Context.Device.System)); - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm Context.ResponseData.Write(BitConverter.ToUInt32(Address.GetAddressBytes())); - Context.Device.Log.PrintInfo(LogClass.ServiceNifm, $"Console's local IP is \"{Address}\"."); + Logger.PrintInfo(LogClass.ServiceNifm, $"Console's local IP is \"{Address}\"."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs index 7bd30ff905..9832786040 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs @@ -1,12 +1,12 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Nifm { - class IRequest : IpcService, IDisposable + class IRequest : IpcService { private Dictionary m_Commands; @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm private KEvent Event0; private KEvent Event1; - public IRequest() + public IRequest(Horizon System) { m_Commands = new Dictionary() { @@ -27,30 +27,37 @@ namespace Ryujinx.HLE.HOS.Services.Nifm { 11, SetConnectionConfirmationOption } }; - Event0 = new KEvent(); - Event1 = new KEvent(); + Event0 = new KEvent(System); + Event1 = new KEvent(System); } public long GetRequestState(ServiceCtx Context) { Context.ResponseData.Write(1); - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } public long GetResult(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } public long GetSystemEventReadableHandles(ServiceCtx Context) { - int Handle0 = Context.Process.HandleTable.OpenHandle(Event0); - int Handle1 = Context.Process.HandleTable.OpenHandle(Event1); + if (Context.Process.HandleTable.GenerateHandle(Event0.ReadableEvent, out int Handle0) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + if (Context.Process.HandleTable.GenerateHandle(Event1.ReadableEvent, out int Handle1) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle0, Handle1); @@ -59,37 +66,23 @@ namespace Ryujinx.HLE.HOS.Services.Nifm public long Cancel(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } public long Submit(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } public long SetConnectionConfirmationOption(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - Event0.Dispose(); - Event1.Dispose(); - } - } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs index b8455d4144..82fce6b983 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Ns @@ -23,14 +23,14 @@ namespace Ryujinx.HLE.HOS.Services.Ns { Context.ResponseData.Write(0); - Context.Device.Log.PrintStub(LogClass.ServiceNs, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNs, "Stubbed."); return 0; } public static long ListAddOnContent(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNs, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNs, "Stubbed."); //TODO: This is supposed to write a u32 array aswell. //It's unknown what it contains. diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index bfc76931de..b8ae11cea1 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; @@ -6,13 +7,12 @@ using Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu; using Ryujinx.HLE.HOS.Services.Nv.NvHostChannel; using Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvMap; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Nv { - class INvDrvServices : IpcService, IDisposable + class INvDrvServices : IpcService { private delegate int IoctlProcessor(ServiceCtx Context, int Cmd); @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv private KEvent Event; - public INvDrvServices() + public INvDrvServices(Horizon System) { m_Commands = new Dictionary() { @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv { 13, FinishInitialize } }; - Event = new KEvent(); + Event = new KEvent(System); } static INvDrvServices() @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv { long NamePtr = Context.Request.SendBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr); + string Name = MemoryHelper.ReadAsciiString(Context.Memory, NamePtr); int Fd = Fds.Add(Context.Process, new NvFd(Name)); @@ -123,7 +123,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv int EventId = Context.RequestData.ReadInt32(); //TODO: Use Fd/EventId, different channels have different events. - int Handle = Context.Process.HandleTable.OpenHandle(Event); + if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -143,7 +146,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv public long FinishInitialize(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return 0; } @@ -177,14 +180,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv { if (CmdIn(Cmd) && Context.Request.GetBufferType0x21().Position == 0) { - Context.Device.Log.PrintError(LogClass.ServiceNv, "Input buffer is null!"); + Logger.PrintError(LogClass.ServiceNv, "Input buffer is null!"); return NvResult.InvalidInput; } if (CmdOut(Cmd) && Context.Request.GetBufferType0x22().Position == 0) { - Context.Device.Log.PrintError(LogClass.ServiceNv, "Output buffer is null!"); + Logger.PrintError(LogClass.ServiceNv, "Output buffer is null!"); return NvResult.InvalidInput; } @@ -214,18 +217,5 @@ namespace Ryujinx.HLE.HOS.Services.Nv NvMapIoctl.UnloadProcess(Process); } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - Event.Dispose(); - } - } } } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs index 95eb5b9860..fed4104229 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs @@ -1,7 +1,7 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; using Ryujinx.HLE.HOS.Services.Nv.NvMap; -using Ryujinx.HLE.Logging; using System; using System.Collections.Concurrent; @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvGpuASAllocSpace Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvGpuASAllocSpace Args = MemoryHelper.Read(Context.Memory, InputPosition); NvGpuASCtx ASCtx = GetASCtx(Context); @@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS { Args.Offset = 0; - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {Size:x16}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {Size:x16}!"); Result = NvResult.OutOfMemory; } @@ -88,7 +88,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS } } - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return Result; } @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvGpuASAllocSpace Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvGpuASAllocSpace Args = MemoryHelper.Read(Context.Memory, InputPosition); NvGpuASCtx ASCtx = GetASCtx(Context); @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS } else { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, + Logger.PrintWarning(LogClass.ServiceNv, $"Failed to free offset 0x{Args.Offset:x16} size 0x{Size:x16}!"); Result = NvResult.InvalidInput; @@ -130,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvGpuASUnmapBuffer Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvGpuASUnmapBuffer Args = MemoryHelper.Read(Context.Memory, InputPosition); NvGpuASCtx ASCtx = GetASCtx(Context); @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS } else { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); } } @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvGpuASMapBufferEx Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvGpuASMapBufferEx Args = MemoryHelper.Read(Context.Memory, InputPosition); NvGpuASCtx ASCtx = GetASCtx(Context); @@ -167,7 +167,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); return NvResult.InvalidInput; } @@ -188,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS { string Msg = string.Format(MapErrorMsg, VA, Args.MappingSize); - Context.Device.Log.PrintWarning(LogClass.ServiceNv, Msg); + Logger.PrintWarning(LogClass.ServiceNv, Msg); return NvResult.InvalidInput; } @@ -197,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS } else { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Address 0x{Args.Offset:x16} not mapped!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Address 0x{Args.Offset:x16} not mapped!"); return NvResult.InvalidInput; } @@ -231,7 +231,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS { string Msg = string.Format(MapErrorMsg, Args.Offset, Size); - Context.Device.Log.PrintWarning(LogClass.ServiceNv, Msg); + Logger.PrintWarning(LogClass.ServiceNv, Msg); Result = NvResult.InvalidInput; } @@ -245,7 +245,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS { Args.Offset = 0; - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{Size:x16}!"); Result = NvResult.InvalidInput; } @@ -255,7 +255,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS } } - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return Result; } @@ -265,7 +265,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -275,7 +275,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -288,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS for (int Index = 0; Index < Count; Index++, InputPosition += 0x14) { - NvGpuASRemap Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvGpuASRemap Args = MemoryHelper.Read(Context.Memory, InputPosition); NvGpuVmm Vmm = GetASCtx(Context).Vmm; @@ -296,7 +296,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); return NvResult.InvalidInput; } @@ -306,7 +306,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS if (Result < 0) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, + Logger.PrintWarning(LogClass.ServiceNv, $"Page 0x{Args.Offset:x16} size 0x{Args.Pages:x16} not allocated!"); return NvResult.InvalidInput; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs index 5ae45c1e9d..7ee770f4c9 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs @@ -1,5 +1,5 @@ using ChocolArm64.Memory; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using System; using System.Diagnostics; @@ -44,9 +44,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu Args.Size = 1; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -68,9 +68,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu Args.SubregionHeightAlignPixels = 0x40; Args.SubregionCount = 0x10; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvGpuGpuGetCharacteristics Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvGpuGpuGetCharacteristics Args = MemoryHelper.Read(Context.Memory, InputPosition); Args.BufferSize = 0xa0; @@ -130,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu Args.ChipName = 0x6230326d67; Args.GrCompbitStoreBaseHw = 0x0; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } @@ -140,14 +140,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvGpuGpuGetTpcMasks Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvGpuGpuGetTpcMasks Args = MemoryHelper.Read(Context.Memory, InputPosition); if (Args.MaskBufferSize != 0) { Args.TpcMask = 3; } - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } @@ -161,9 +161,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu Args.Slot = 0x07; Args.Mask = 0x01; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs index f4ed48217b..39f39d4569 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -1,7 +1,7 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; -using Ryujinx.HLE.Logging; using System; using System.Collections.Concurrent; @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvHostChannelSubmitGpfifo Args = MemoryHelper.Read(Context.Memory, InputPosition); NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;; @@ -100,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel Args.SyncptId = 0; Args.SyncptValue = 0; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } @@ -110,7 +110,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -130,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -140,7 +140,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -150,7 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -160,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvHostChannelSubmitGpfifo Args = MemoryHelper.Read(Context.Memory, InputPosition); NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;; @@ -174,22 +174,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel Args.SyncptId = 0; Args.SyncptValue = 0; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } private static void PushGpfifo(ServiceCtx Context, NvGpuVmm Vmm, long Gpfifo) { - long VA = Gpfifo & 0xff_ffff_ffff; - - int Size = (int)(Gpfifo >> 40) & 0x7ffffc; - - byte[] Data = Vmm.ReadBytes(VA, Size); - - NvGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data); - - Context.Device.Gpu.Fifo.PushBuffer(Vmm, PushBuffer); + Context.Device.Gpu.Pusher.Push(Vmm, Gpfifo); } public static NvChannel GetChannel(ServiceCtx Context, NvChannelName Channel) diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs index 2bfe88821b..6cb1474105 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -1,5 +1,5 @@ using ChocolArm64.Memory; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using System; using System.Collections.Concurrent; using System.Text; @@ -84,8 +84,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - string Domain = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0, 0x41); - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0x41, 0x41); + string Domain = MemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0, 0x41); + string Name = MemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0x41, 0x41); if (Set.NxSettings.Settings.TryGetValue($"{Domain}!{Name}", out object NvSetting)) { @@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl { if (StringValue.Length > 0x100) { - Context.Device.Log.PrintError(Logging.LogClass.ServiceNv, $"{Domain}!{Name} String value size is too big!"); + Logger.PrintError(LogClass.ServiceNv, $"{Domain}!{Name} String value size is too big!"); } else { @@ -118,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl Context.Memory.WriteBytes(OutputPosition + 0x82, SettingBuffer); - Context.Device.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}"); + Logger.PrintDebug(LogClass.ServiceNv, $"Got setting {Domain}!{Name}"); } return NvResult.Success; @@ -144,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl int EventId = Context.Memory.ReadInt32(InputPosition); - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvHostCtrlSyncptRead Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvHostCtrlSyncptRead Args = MemoryHelper.Read(Context.Memory, InputPosition); if ((uint)Args.Id >= NvHostSyncpt.SyncptsCount) { @@ -170,7 +170,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl Args.Value = GetUserCtx(Context).Syncpt.GetMin(Args.Id); } - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvHostCtrlSyncptWait Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvHostCtrlSyncptWait Args = MemoryHelper.Read(Context.Memory, InputPosition); NvHostSyncpt Syncpt = GetUserCtx(Context).Syncpt; @@ -201,7 +201,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl } else { - Context.Device.Log.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + Args.Timeout + "ms..."); + Logger.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + Args.Timeout + "ms..."); using (ManualResetEvent WaitEvent = new ManualResetEvent(false)) { @@ -232,7 +232,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl } } - Context.Device.Log.PrintDebug(LogClass.ServiceNv, "Resuming..."); + Logger.PrintDebug(LogClass.ServiceNv, "Resuming..."); } if (Extended) @@ -248,7 +248,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvHostCtrlSyncptWaitEx Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvHostCtrlSyncptWaitEx Args = MemoryHelper.Read(Context.Memory, InputPosition); if ((uint)Args.Id >= NvHostSyncpt.SyncptsCount) { @@ -257,7 +257,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl void WriteArgs() { - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); } NvHostSyncpt Syncpt = GetUserCtx(Context).Syncpt; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs index 38da2889eb..f5378ef7fb 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs @@ -1,6 +1,6 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System.Collections.Concurrent; @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap case 0x010e: return GetId (Context); } - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Unsupported Ioctl command 0x{Cmd:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Unsupported Ioctl command 0x{Cmd:x8}!"); return NvResult.NotSupported; } @@ -39,11 +39,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvMapCreate Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvMapCreate Args = MemoryHelper.Read(Context.Memory, InputPosition); if (Args.Size == 0) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid size 0x{Args.Size:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid size 0x{Args.Size:x8}!"); return NvResult.InvalidInput; } @@ -52,9 +52,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap Args.Handle = AddNvMap(Context, new NvMapHandle(Size)); - Context.Device.Log.PrintInfo(LogClass.ServiceNv, $"Created map {Args.Handle} with size 0x{Size:x8}!"); + Logger.PrintInfo(LogClass.ServiceNv, $"Created map {Args.Handle} with size 0x{Size:x8}!"); - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } @@ -64,13 +64,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvMapFromId Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvMapFromId Args = MemoryHelper.Read(Context.Memory, InputPosition); NvMapHandle Map = GetNvMap(Context, Args.Id); if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap Args.Handle = Args.Id; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } @@ -89,20 +89,20 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvMapAlloc Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvMapAlloc Args = MemoryHelper.Read(Context.Memory, InputPosition); NvMapHandle Map = GetNvMap(Context, Args.Handle); if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } if ((Args.Align & (Args.Align - 1)) != 0) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid alignment 0x{Args.Align:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid alignment 0x{Args.Align:x8}!"); return NvResult.InvalidInput; } @@ -143,7 +143,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap } } - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return Result; } @@ -153,13 +153,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvMapFree Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvMapFree Args = MemoryHelper.Read(Context.Memory, InputPosition); NvMapHandle Map = GetNvMap(Context, Args.Handle); if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap { DeleteNvMap(Context, Args.Handle); - Context.Device.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!"); + Logger.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!"); Args.Address = Map.Address; Args.Flags = 0; @@ -181,7 +181,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap Args.Size = Map.Size; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } @@ -191,13 +191,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvMapParam Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvMapParam Args = MemoryHelper.Read(Context.Memory, InputPosition); NvMapHandle Map = GetNvMap(Context, Args.Handle); if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } @@ -215,7 +215,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap default: return NvResult.InvalidInput; } - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } @@ -225,20 +225,20 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - NvMapGetId Args = AMemoryHelper.Read(Context.Memory, InputPosition); + NvMapGetId Args = MemoryHelper.Read(Context.Memory, InputPosition); NvMapHandle Map = GetNvMap(Context, Args.Handle); if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } Args.Id = Args.Handle; - AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + MemoryHelper.Write(Context.Memory, OutputPosition, Args); return NvResult.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlService.cs b/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlService.cs index d484d312f3..3e9d276ae2 100644 --- a/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlService.cs +++ b/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Pctl @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl } else { - Context.Device.Log.PrintWarning(LogClass.ServicePctl, "Service is already initialized!"); + Logger.PrintWarning(LogClass.ServicePctl, "Service is already initialized!"); } return 0; diff --git a/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs index 92821217ba..d73adc0efd 100644 --- a/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs @@ -1,5 +1,7 @@ using Ryujinx.HLE.HOS.Font; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Pl @@ -65,7 +67,10 @@ namespace Ryujinx.HLE.HOS.Services.Pl { Context.Device.System.Font.EnsureInitialized(); - int Handle = Context.Process.HandleTable.OpenHandle(Context.Device.System.FontSharedMem); + if (Context.Process.HandleTable.GenerateHandle(Context.Device.System.FontSharedMem, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs index fa39854d1d..cc96a5c920 100644 --- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs +++ b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Prepo @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Prepo public static long SaveReportWithUser(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServicePrepo, "Stubbed."); + Logger.PrintStub(LogClass.ServicePrepo, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs b/Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs new file mode 100644 index 0000000000..0b45e3c9df --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs @@ -0,0 +1,60 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Psm +{ + class IPsmServer : IpcService + { + enum ChargerType : int + { + None, + ChargerOrDock, + UsbC + } + + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public IPsmServer() + { + m_Commands = new Dictionary() + { + { 0, GetBatteryChargePercentage }, + { 1, GetChargerType }, + { 7, OpenSession } + }; + } + + // GetBatteryChargePercentage() -> u32 + public static long GetBatteryChargePercentage(ServiceCtx Context) + { + int ChargePercentage = 100; + + Context.ResponseData.Write(ChargePercentage); + + Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. ChargePercentage: {ChargePercentage}"); + + return 0; + } + + // GetChargerType() -> u32 + public static long GetChargerType(ServiceCtx Context) + { + Context.ResponseData.Write((int)ChargerType.ChargerOrDock); + + Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. ChargerType: {ChargerType.ChargerOrDock}"); + + return 0; + } + + // OpenSession() -> IPsmSession + public long OpenSession(ServiceCtx Context) + { + MakeObject(Context, new IPsmSession(Context.Device.System)); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs b/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs new file mode 100644 index 0000000000..30e5d53220 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs @@ -0,0 +1,96 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Psm +{ + class IPsmSession : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private KEvent StateChangeEvent; + private int StateChangeEventHandle; + + public IPsmSession(Horizon System) + { + m_Commands = new Dictionary() + { + { 0, BindStateChangeEvent }, + { 1, UnbindStateChangeEvent }, + { 2, SetChargerTypeChangeEventEnabled }, + { 3, SetPowerSupplyChangeEventEnabled }, + { 4, SetBatteryVoltageStateChangeEventEnabled } + }; + + StateChangeEvent = new KEvent(System); + StateChangeEventHandle = -1; + } + + // BindStateChangeEvent() -> KObject + public long BindStateChangeEvent(ServiceCtx Context) + { + if (StateChangeEventHandle == -1) + { + KernelResult ResultCode = Context.Process.HandleTable.GenerateHandle(StateChangeEvent, out int StateChangeEventHandle); + + if (ResultCode != KernelResult.Success) + { + return (long)ResultCode; + } + } + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(StateChangeEventHandle); + + Logger.PrintStub(LogClass.ServicePsm, "Stubbed."); + + return 0; + } + + // UnbindStateChangeEvent() + public long UnbindStateChangeEvent(ServiceCtx Context) + { + if (StateChangeEventHandle != -1) + { + Context.Process.HandleTable.CloseHandle(StateChangeEventHandle); + StateChangeEventHandle = -1; + } + + Logger.PrintStub(LogClass.ServicePsm, "Stubbed."); + + return 0; + } + + // SetChargerTypeChangeEventEnabled(u8) + public long SetChargerTypeChangeEventEnabled(ServiceCtx Context) + { + bool ChargerTypeChangeEventEnabled = Context.RequestData.ReadBoolean(); + + Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. ChargerTypeChangeEventEnabled: {ChargerTypeChangeEventEnabled}"); + + return 0; + } + + // SetPowerSupplyChangeEventEnabled(u8) + public long SetPowerSupplyChangeEventEnabled(ServiceCtx Context) + { + bool PowerSupplyChangeEventEnabled = Context.RequestData.ReadBoolean(); + + Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. PowerSupplyChangeEventEnabled: {PowerSupplyChangeEventEnabled}"); + + return 0; + } + + // SetBatteryVoltageStateChangeEventEnabled(u8) + public long SetBatteryVoltageStateChangeEventEnabled(ServiceCtx Context) + { + bool BatteryVoltageStateChangeEventEnabled = Context.RequestData.ReadBoolean(); + + Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. BatteryVoltageStateChangeEventEnabled: {BatteryVoltageStateChangeEventEnabled}"); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index 5e65d1d104..d91583d62b 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -6,6 +6,8 @@ using Ryujinx.HLE.HOS.Services.Bsd; using Ryujinx.HLE.HOS.Services.Caps; using Ryujinx.HLE.HOS.Services.FspSrv; using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.HLE.HOS.Services.Irs; +using Ryujinx.HLE.HOS.Services.Ldr; using Ryujinx.HLE.HOS.Services.Lm; using Ryujinx.HLE.HOS.Services.Mm; using Ryujinx.HLE.HOS.Services.Nfp; @@ -14,6 +16,7 @@ using Ryujinx.HLE.HOS.Services.Nv; using Ryujinx.HLE.HOS.Services.Pctl; using Ryujinx.HLE.HOS.Services.Pl; using Ryujinx.HLE.HOS.Services.Prepo; +using Ryujinx.HLE.HOS.Services.Psm; using Ryujinx.HLE.HOS.Services.Set; using Ryujinx.HLE.HOS.Services.Sfdnsres; using Ryujinx.HLE.HOS.Services.Sm; @@ -26,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services { static class ServiceFactory { - public static IpcService MakeService(string Name) + public static IpcService MakeService(Horizon System, string Name) { switch (Name) { @@ -70,10 +73,10 @@ namespace Ryujinx.HLE.HOS.Services return new Bcat.IServiceCreator(); case "bsd:s": - return new IClient(); + return new IClient(true); case "bsd:u": - return new IClient(); + return new IClient(false); case "caps:a": return new IAlbumAccessorService(); @@ -94,7 +97,16 @@ namespace Ryujinx.HLE.HOS.Services return new IFileSystemProxy(); case "hid": - return new IHidServer(); + return new IHidServer(System); + + case "irs": + return new IIrSensorServer(); + + case "ldr:ro": + return new IRoInterface(); + + case "hwopus": + return new IHardwareOpusDecoderManager(); case "lm": return new ILogService(); @@ -118,10 +130,10 @@ namespace Ryujinx.HLE.HOS.Services return new IVulnerabilityManagerInterface(); case "nvdrv": - return new INvDrvServices(); + return new INvDrvServices(System); case "nvdrv:a": - return new INvDrvServices(); + return new INvDrvServices(System); case "pctl:s": return new IParentalControlServiceFactory(); @@ -144,6 +156,9 @@ namespace Ryujinx.HLE.HOS.Services case "prepo:u": return new IPrepoService(); + case "psm": + return new IPsmServer(); + case "set": return new ISettingsServer(); diff --git a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs index dc1469674c..070a4d5e4d 100644 --- a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; using System; @@ -115,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Set { if (StringValue.Length + 1 > ReplySize) { - Context.Device.Log.PrintError(Logging.LogClass.ServiceSet, $"{AskedSetting} String value size is too big!"); + Logger.PrintError(LogClass.ServiceSet, $"{AskedSetting} String value size is too big!"); } else { @@ -138,11 +139,11 @@ namespace Ryujinx.HLE.HOS.Services.Set Context.Memory.WriteBytes(ReplyPos, SettingBuffer); - Context.Device.Log.PrintDebug(Logging.LogClass.ServiceSet, $"{AskedSetting} set value: {NxSetting} as {NxSetting.GetType()}"); + Logger.PrintDebug(LogClass.ServiceSet, $"{AskedSetting} set value: {NxSetting} as {NxSetting.GetType()}"); } else { - Context.Device.Log.PrintError(Logging.LogClass.ServiceSet, $"{AskedSetting} not found!"); + Logger.PrintError(LogClass.ServiceSet, $"{AskedSetting} not found!"); } return 0; diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs new file mode 100644 index 0000000000..65d5457707 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.HLE.HOS.Services.Sfdnsres +{ + enum GaiError + { + Success, + AddressFamily, + Again, + BadFlags, + Fail, + Family, + Memory, + NoData, + NoName, + Service, + SocketType, + System, + BadHints, + Protocol, + Overflow, + Max, + } +} diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs index 26dbedf441..a351e3deca 100644 --- a/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs @@ -1,5 +1,12 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; +using System; using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; + +using static Ryujinx.HLE.HOS.ErrorCode; namespace Ryujinx.HLE.HOS.Services.Sfdnsres { @@ -13,8 +20,380 @@ namespace Ryujinx.HLE.HOS.Services.Sfdnsres { m_Commands = new Dictionary() { - //... + { 0, SetDnsAddressesPrivate }, + { 1, GetDnsAddressesPrivate }, + { 2, GetHostByName }, + { 3, GetHostByAddress }, + { 4, GetHostStringError }, + { 5, GetGaiStringError }, + { 8, RequestCancelHandle }, + { 9, CancelSocketCall }, + { 11, ClearDnsAddresses }, }; } + + private long SerializeHostEnt(ServiceCtx Context, IPHostEntry HostEntry, List Addresses = null) + { + long OriginalBufferPosition = Context.Request.ReceiveBuff[0].Position; + long BufferPosition = OriginalBufferPosition; + long BufferSize = Context.Request.ReceiveBuff[0].Size; + + string HostName = HostEntry.HostName + '\0'; + + // h_name + Context.Memory.WriteBytes(BufferPosition, Encoding.ASCII.GetBytes(HostName)); + BufferPosition += HostName.Length; + + // h_aliases list size + Context.Memory.WriteInt32(BufferPosition, IPAddress.HostToNetworkOrder(HostEntry.Aliases.Length)); + BufferPosition += 4; + + // Actual aliases + foreach (string Alias in HostEntry.Aliases) + { + Context.Memory.WriteBytes(BufferPosition, Encoding.ASCII.GetBytes(Alias + '\0')); + BufferPosition += Alias.Length + 1; + } + + // h_addrtype but it's a short (also only support IPv4) + Context.Memory.WriteInt16(BufferPosition, IPAddress.HostToNetworkOrder((short)2)); + BufferPosition += 2; + + // h_length but it's a short + Context.Memory.WriteInt16(BufferPosition, IPAddress.HostToNetworkOrder((short)4)); + BufferPosition += 2; + + // Ip address count, we can only support ipv4 (blame Nintendo) + Context.Memory.WriteInt32(BufferPosition, Addresses != null ? IPAddress.HostToNetworkOrder(Addresses.Count) : 0); + BufferPosition += 4; + + if (Addresses != null) + { + foreach (IPAddress Ip in Addresses) + { + Context.Memory.WriteInt32(BufferPosition, IPAddress.HostToNetworkOrder(BitConverter.ToInt32(Ip.GetAddressBytes(), 0))); + BufferPosition += 4; + } + } + + return BufferPosition - OriginalBufferPosition; + } + + private string GetGaiStringErrorFromErrorCode(GaiError ErrorCode) + { + if (ErrorCode > GaiError.Max) + { + ErrorCode = GaiError.Max; + } + + switch (ErrorCode) + { + case GaiError.AddressFamily: + return "Address family for hostname not supported"; + case GaiError.Again: + return "Temporary failure in name resolution"; + case GaiError.BadFlags: + return "Invalid value for ai_flags"; + case GaiError.Fail: + return "Non-recoverable failure in name resolution"; + case GaiError.Family: + return "ai_family not supported"; + case GaiError.Memory: + return "Memory allocation failure"; + case GaiError.NoData: + return "No address associated with hostname"; + case GaiError.NoName: + return "hostname nor servname provided, or not known"; + case GaiError.Service: + return "servname not supported for ai_socktype"; + case GaiError.SocketType: + return "ai_socktype not supported"; + case GaiError.System: + return "System error returned in errno"; + case GaiError.BadHints: + return "Invalid value for hints"; + case GaiError.Protocol: + return "Resolved protocol is unknown"; + case GaiError.Overflow: + return "Argument buffer overflow"; + case GaiError.Max: + return "Unknown error"; + default: + return "Success"; + } + } + + private string GetHostStringErrorFromErrorCode(NetDBError ErrorCode) + { + if (ErrorCode <= NetDBError.Internal) + { + return "Resolver internal error"; + } + + switch (ErrorCode) + { + case NetDBError.Success: + return "Resolver Error 0 (no error)"; + case NetDBError.HostNotFound: + return "Unknown host"; + case NetDBError.TryAgain: + return "Host name lookup failure"; + case NetDBError.NoRecovery: + return "Unknown server error"; + case NetDBError.NoData: + return "No address associated with name"; + default: + return "Unknown resolver error"; + } + } + + private List GetIPV4Addresses(IPHostEntry HostEntry) + { + List Result = new List(); + foreach (IPAddress Ip in HostEntry.AddressList) + { + if (Ip.AddressFamily == AddressFamily.InterNetwork) + Result.Add(Ip); + } + return Result; + } + + // SetDnsAddressesPrivate(u32, buffer) + public long SetDnsAddressesPrivate(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + long BufferPosition = Context.Request.SendBuff[0].Position; + long BufferSize = Context.Request.SendBuff[0].Size; + + // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness. + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + + return MakeError(ErrorModule.Os, 1023); + } + + // GetDnsAddressPrivate(u32) -> buffer + public long GetDnsAddressesPrivate(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + + // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness. + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + + return MakeError(ErrorModule.Os, 1023); + } + + // GetHostByName(u8, u32, u64, pid, buffer) -> (u32, u32, u32, buffer) + public long GetHostByName(ServiceCtx Context) + { + byte[] RawName = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, Context.Request.SendBuff[0].Size); + string Name = Encoding.ASCII.GetString(RawName).TrimEnd('\0'); + + // TODO: use params + bool EnableNsdResolve = Context.RequestData.ReadInt32() == 1; + int TimeOut = Context.RequestData.ReadInt32(); + ulong PidPlaceholder = Context.RequestData.ReadUInt64(); + + IPHostEntry HostEntry = null; + + NetDBError NetDBErrorCode = NetDBError.Success; + GaiError Errno = GaiError.Overflow; + long SerializedSize = 0; + + if (Name.Length <= 255) + { + try + { + HostEntry = Dns.GetHostEntry(Name); + } + catch (SocketException Exception) + { + NetDBErrorCode = NetDBError.Internal; + + if (Exception.ErrorCode == 11001) + { + NetDBErrorCode = NetDBError.HostNotFound; + Errno = GaiError.NoData; + } + else if (Exception.ErrorCode == 11002) + { + NetDBErrorCode = NetDBError.TryAgain; + } + else if (Exception.ErrorCode == 11003) + { + NetDBErrorCode = NetDBError.NoRecovery; + } + else if (Exception.ErrorCode == 11004) + { + NetDBErrorCode = NetDBError.NoData; + } + else if (Exception.ErrorCode == 10060) + { + Errno = GaiError.Again; + } + } + } + else + { + NetDBErrorCode = NetDBError.HostNotFound; + } + + if (HostEntry != null) + { + Errno = GaiError.Success; + + List Addresses = GetIPV4Addresses(HostEntry); + + if (Addresses.Count == 0) + { + Errno = GaiError.NoData; + NetDBErrorCode = NetDBError.NoAddress; + } + else + { + SerializedSize = SerializeHostEnt(Context, HostEntry, Addresses); + } + } + + Context.ResponseData.Write((int)NetDBErrorCode); + Context.ResponseData.Write((int)Errno); + Context.ResponseData.Write(SerializedSize); + + return 0; + } + + // GetHostByAddr(u32, u32, u32, u64, pid, buffer) -> (u32, u32, u32, buffer) + public long GetHostByAddress(ServiceCtx Context) + { + byte[] RawIp = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, Context.Request.SendBuff[0].Size); + + // TODO: use params + uint SocketLength = Context.RequestData.ReadUInt32(); + uint Type = Context.RequestData.ReadUInt32(); + int TimeOut = Context.RequestData.ReadInt32(); + ulong PidPlaceholder = Context.RequestData.ReadUInt64(); + + IPHostEntry HostEntry = null; + + NetDBError NetDBErrorCode = NetDBError.Success; + GaiError Errno = GaiError.AddressFamily; + long SerializedSize = 0; + + if (RawIp.Length == 4) + { + try + { + IPAddress Address = new IPAddress(RawIp); + + HostEntry = Dns.GetHostEntry(Address); + } + catch (SocketException Exception) + { + NetDBErrorCode = NetDBError.Internal; + if (Exception.ErrorCode == 11001) + { + NetDBErrorCode = NetDBError.HostNotFound; + Errno = GaiError.NoData; + } + else if (Exception.ErrorCode == 11002) + { + NetDBErrorCode = NetDBError.TryAgain; + } + else if (Exception.ErrorCode == 11003) + { + NetDBErrorCode = NetDBError.NoRecovery; + } + else if (Exception.ErrorCode == 11004) + { + NetDBErrorCode = NetDBError.NoData; + } + else if (Exception.ErrorCode == 10060) + { + Errno = GaiError.Again; + } + } + } + else + { + NetDBErrorCode = NetDBError.NoAddress; + } + + if (HostEntry != null) + { + Errno = GaiError.Success; + SerializedSize = SerializeHostEnt(Context, HostEntry, GetIPV4Addresses(HostEntry)); + } + + Context.ResponseData.Write((int)NetDBErrorCode); + Context.ResponseData.Write((int)Errno); + Context.ResponseData.Write(SerializedSize); + + return 0; + } + + // GetHostStringError(u32) -> buffer + public long GetHostStringError(ServiceCtx Context) + { + long ResultCode = MakeError(ErrorModule.Os, 1023); + NetDBError ErrorCode = (NetDBError)Context.RequestData.ReadInt32(); + string ErrorString = GetHostStringErrorFromErrorCode(ErrorCode); + + if (ErrorString.Length + 1 <= Context.Request.ReceiveBuff[0].Size) + { + ResultCode = 0; + Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(ErrorString + '\0')); + } + + return ResultCode; + } + + // GetGaiStringError(u32) -> buffer + public long GetGaiStringError(ServiceCtx Context) + { + long ResultCode = MakeError(ErrorModule.Os, 1023); + GaiError ErrorCode = (GaiError)Context.RequestData.ReadInt32(); + string ErrorString = GetGaiStringErrorFromErrorCode(ErrorCode); + + if (ErrorString.Length + 1 <= Context.Request.ReceiveBuff[0].Size) + { + ResultCode = 0; + Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(ErrorString + '\0')); + } + + return ResultCode; + } + + // RequestCancelHandle(u64, pid) -> u32 + public long RequestCancelHandle(ServiceCtx Context) + { + ulong Unknown0 = Context.RequestData.ReadUInt64(); + + Context.ResponseData.Write(0); + + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + + return 0; + } + + // CancelSocketCall(u32, u64, pid) + public long CancelSocketCall(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + ulong Unknown1 = Context.RequestData.ReadUInt64(); + + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); + + return 0; + } + + // ClearDnsAddresses(u32) + public long ClearDnsAddresses(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + + return 0; + } } } diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs new file mode 100644 index 0000000000..6c1b7825d2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.HLE.HOS.Services.Sfdnsres +{ + enum NetDBError + { + Internal = -1, + Success, + HostNotFound, + TryAgain, + NoRecovery, + NoData, + NoAddress = NoData, + } +} diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index efa64ee7dd..0c26b7d9dd 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Sm @@ -57,9 +58,12 @@ namespace Ryujinx.HLE.HOS.Services.Sm return 0; } - KSession Session = new KSession(ServiceFactory.MakeService(Name), Name); + KSession Session = new KSession(ServiceFactory.MakeService(Context.Device.System, Name), Name); - int Handle = Context.Process.HandleTable.OpenHandle(Session); + if (Context.Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs b/Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs new file mode 100644 index 0000000000..8f3a0649b2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs @@ -0,0 +1,20 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Ssl +{ + class ISslContext : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public ISslContext() + { + m_Commands = new Dictionary() + { + //... + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs index b59527f707..3046aab7a3 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Ssl @@ -14,15 +14,30 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { m_Commands = new Dictionary() { + { 0, CreateContext }, { 5, SetInterfaceVersion } }; } + // CreateContext(nn::ssl::sf::SslVersion, u64, pid) -> object + public long CreateContext(ServiceCtx Context) + { + int SslVersion = Context.RequestData.ReadInt32(); + long Unknown = Context.RequestData.ReadInt64(); + + Logger.PrintStub(LogClass.ServiceSsl, $"Stubbed. SslVersion: {SslVersion} - Unknown: {Unknown}"); + + MakeObject(Context, new ISslContext()); + + return 0; + } + + // SetInterfaceVersion(u32) public long SetInterfaceVersion(ServiceCtx Context) { int Version = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceSsl, "Stubbed."); + Logger.PrintStub(LogClass.ServiceSsl, $"Stubbed. Version: {Version}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs index 6df28659d1..0e321e44c2 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.Text; @@ -110,7 +110,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (BufferSize != 0x4000) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); + Logger.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); } long ResultCode = 0; @@ -132,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Services.Time } catch (TimeZoneNotFoundException) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); + Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); ResultCode = MakeError(ErrorModule.Time, 0x3dd); } @@ -170,7 +170,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (BufferSize != 0x4000) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); + Logger.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); } // TODO: Reverse the TZif2 conversion in PCV to make this match with real hardware. @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Services.Time } catch (TimeZoneNotFoundException) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); + Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); ResultCode = MakeError(ErrorModule.Time, 0x3dd); } @@ -220,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (BufferSize != 0x4000) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); + Logger.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); } // TODO: Reverse the TZif2 conversion in PCV to make this match with real hardware. @@ -239,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Services.Time } catch (TimeZoneNotFoundException) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); + Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); ResultCode = MakeError(ErrorModule.Time, 0x3dd); } diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs index 3006b73a50..5d1ddd84b3 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs @@ -1,6 +1,8 @@ using ChocolArm64.Memory; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; using System.Collections.Generic; +using System; using System.IO; using System.Text; @@ -41,7 +43,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi public long GetRelayService(ServiceCtx Context) { - MakeObject(Context, new IHOSBinderDriver(Context.Device.Gpu.Renderer)); + MakeObject(Context, new IHOSBinderDriver( + Context.Device.System, + Context.Device.Gpu.Renderer)); return 0; } @@ -62,7 +66,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi public long GetIndirectDisplayTransactionService(ServiceCtx Context) { - MakeObject(Context, new IHOSBinderDriver(Context.Device.Gpu.Renderer)); + MakeObject(Context, new IHOSBinderDriver( + Context.Device.System, + Context.Device.Gpu.Renderer)); return 0; } @@ -71,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi { long RecBuffPtr = Context.Request.ReceiveBuff[0].Position; - AMemoryHelper.FillWithZeros(Context.Memory, RecBuffPtr, 0x60); + MemoryHelper.FillWithZeros(Context.Memory, RecBuffPtr, 0x60); //Add only the default display to buffer Context.Memory.WriteBytes(RecBuffPtr, Encoding.ASCII.GetBytes("Default")); @@ -174,7 +180,10 @@ namespace Ryujinx.HLE.HOS.Services.Vi { string Name = GetDisplayName(Context); - int Handle = Context.Process.HandleTable.OpenHandle(Context.Device.System.VsyncEvent); + if (Context.Process.HandleTable.GenerateHandle(Context.Device.System.VsyncEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs index bf5f20a2a1..09a37b0f7b 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs @@ -13,11 +13,11 @@ namespace Ryujinx.HLE.HOS.Services.Vi public override IReadOnlyDictionary Commands => m_Commands; - private KEvent ReleaseEvent; + private KEvent BinderEvent; private NvFlinger Flinger; - public IHOSBinderDriver(IGalRenderer Renderer) + public IHOSBinderDriver(Horizon System, IGalRenderer Renderer) { m_Commands = new Dictionary() { @@ -27,9 +27,11 @@ namespace Ryujinx.HLE.HOS.Services.Vi { 3, TransactParcelAuto } }; - ReleaseEvent = new KEvent(); + BinderEvent = new KEvent(System); - Flinger = new NvFlinger(Renderer, ReleaseEvent); + BinderEvent.ReadableEvent.Signal(); + + Flinger = new NvFlinger(Renderer, BinderEvent); } public long TransactParcel(ServiceCtx Context) @@ -75,7 +77,10 @@ namespace Ryujinx.HLE.HOS.Services.Vi int Id = Context.RequestData.ReadInt32(); uint Unk = Context.RequestData.ReadUInt32(); - int Handle = Context.Process.HandleTable.OpenHandle(ReleaseEvent); + if (Context.Process.HandleTable.GenerateHandle(BinderEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); @@ -91,8 +96,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi { if (Disposing) { - ReleaseEvent.Dispose(); - Flinger.Dispose(); } } diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs index 61f0ffaa51..a78f1812f0 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Vi @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi public static long CreateManagedLayer(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); Context.ResponseData.Write(0L); //LayerId @@ -32,21 +32,21 @@ namespace Ryujinx.HLE.HOS.Services.Vi public long DestroyManagedLayer(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } public static long AddToLayerStack(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } public static long SetLayerVisibility(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs index 5657ba69f3..4545579bef 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Vi @@ -22,14 +22,14 @@ namespace Ryujinx.HLE.HOS.Services.Vi public static long SetLayerZ(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } public static long SetLayerVisibility(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index 2a6918c441..64e0b4a96d 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -1,8 +1,9 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Texture; +using Ryujinx.Graphics.Memory; using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; using Ryujinx.HLE.HOS.Services.Nv.NvMap; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.IO; @@ -19,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Android private Dictionary<(string, int), ServiceProcessParcel> Commands; - private KEvent ReleaseEvent; + private KEvent BinderEvent; private IGalRenderer Renderer; @@ -63,11 +64,11 @@ namespace Ryujinx.HLE.HOS.Services.Android private BufferEntry[] BufferQueue; - private ManualResetEvent WaitBufferFree; + private AutoResetEvent WaitBufferFree; private bool Disposed; - public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent) + public NvFlinger(IGalRenderer Renderer, KEvent BinderEvent) { Commands = new Dictionary<(string, int), ServiceProcessParcel>() { @@ -82,12 +83,12 @@ namespace Ryujinx.HLE.HOS.Services.Android { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } }; - this.Renderer = Renderer; - this.ReleaseEvent = ReleaseEvent; + this.Renderer = Renderer; + this.BinderEvent = BinderEvent; BufferQueue = new BufferEntry[0x40]; - WaitBufferFree = new ManualResetEvent(false); + WaitBufferFree = new AutoResetEvent(false); } public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code) @@ -113,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Services.Android if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq)) { - Context.Device.Log.PrintDebug(LogClass.ServiceVi, $"{InterfaceName} {ProcReq.Method.Name}"); + Logger.PrintDebug(LogClass.ServiceVi, $"{InterfaceName} {ProcReq.Method.Name}"); return ProcReq(Context, Reader); } @@ -219,6 +220,8 @@ namespace Ryujinx.HLE.HOS.Services.Android BufferQueue[Slot].State = BufferState.Free; + WaitBufferFree.Set(); + return MakeReplyParcel(Context, 0); } @@ -301,47 +304,43 @@ namespace Ryujinx.HLE.HOS.Services.Android bool FlipX = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX); bool FlipY = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY); - //Rotation is being ignored + //Note: Rotation is being ignored. int Top = Crop.Top; int Left = Crop.Left; int Right = Crop.Right; int Bottom = Crop.Bottom; - Renderer.QueueAction(() => Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom)); + NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm; - //TODO: Support double buffering here aswell, it is broken for GPU - //frame buffers because it seems to be completely out of sync. - if (Context.Device.Gpu.Engine3d.IsFrameBufferPosition(FbAddr)) + Renderer.QueueAction(() => { - //Frame buffer is rendered to by the GPU, we can just - //bind the frame buffer texture, it's not necessary to read anything. - Renderer.QueueAction(() => Renderer.RenderTarget.Set(FbAddr)); - } - else - { - //Frame buffer is not set on the GPU registers, in this case - //assume that the app is manually writing to it. - TextureInfo Texture = new TextureInfo(FbAddr, FbWidth, FbHeight); + if (!Renderer.Texture.TryGetImage(FbAddr, out GalImage Image)) + { + Image = new GalImage( + FbWidth, + FbHeight, 1, 16, + GalMemoryLayout.BlockLinear, + GalImageFormat.RGBA8 | GalImageFormat.Unorm); + } - byte[] Data = TextureReader.Read(Context.Memory, Texture); + Context.Device.Gpu.ResourceManager.ClearPbCache(); + Context.Device.Gpu.ResourceManager.SendTexture(Vmm, FbAddr, Image); - Renderer.QueueAction(() => Renderer.RenderTarget.Set(Data, FbWidth, FbHeight)); - } + Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom); + Renderer.RenderTarget.Present(FbAddr); - Context.Device.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); + ReleaseBuffer(Slot); + }); } private void ReleaseBuffer(int Slot) { BufferQueue[Slot].State = BufferState.Free; - ReleaseEvent.WaitEvent.Set(); + BinderEvent.ReadableEvent.Signal(); - lock (WaitBufferFree) - { - WaitBufferFree.Set(); - } + WaitBufferFree.Set(); } private int GetFreeSlotBlocking(int Width, int Height) @@ -350,19 +349,14 @@ namespace Ryujinx.HLE.HOS.Services.Android do { - lock (WaitBufferFree) + if ((Slot = GetFreeSlot(Width, Height)) != -1) { - if ((Slot = GetFreeSlot(Width, Height)) != -1) - { - break; - } + break; + } - if (Disposed) - { - break; - } - - WaitBufferFree.Reset(); + if (Disposed) + { + break; } WaitBufferFree.WaitOne(); @@ -409,11 +403,7 @@ namespace Ryujinx.HLE.HOS.Services.Android { Disposed = true; - lock (WaitBufferFree) - { - WaitBufferFree.Set(); - } - + WaitBufferFree.Set(); WaitBufferFree.Dispose(); } } diff --git a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs index ee0e6fea3c..274892c08a 100644 --- a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs +++ b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs @@ -1,11 +1,10 @@ using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Am; -using System; using System.Collections.Concurrent; namespace Ryujinx.HLE.HOS.SystemState { - class AppletStateMgr : IDisposable + class AppletStateMgr { private ConcurrentQueue Messages; @@ -13,11 +12,11 @@ namespace Ryujinx.HLE.HOS.SystemState public KEvent MessageEvent { get; private set; } - public AppletStateMgr() + public AppletStateMgr(Horizon System) { Messages = new ConcurrentQueue(); - MessageEvent = new KEvent(); + MessageEvent = new KEvent(System); } public void SetFocus(bool IsFocused) @@ -33,30 +32,17 @@ namespace Ryujinx.HLE.HOS.SystemState { Messages.Enqueue(Message); - MessageEvent.WaitEvent.Set(); + MessageEvent.ReadableEvent.Signal(); } public bool TryDequeueMessage(out MessageInfo Message) { if (Messages.Count < 2) { - MessageEvent.WaitEvent.Reset(); + MessageEvent.ReadableEvent.Clear(); } return Messages.TryDequeue(out Message); } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - MessageEvent.Dispose(); - } - } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs index bd1dbd78d6..3833ce9ec9 100644 --- a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs +++ b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.Utilities; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -37,6 +38,8 @@ namespace Ryujinx.HLE.HOS.SystemState internal long DesiredLanguageCode { get; private set; } + public TitleLanguage DesiredTitleLanguage { get; private set; } + internal string ActiveAudioOutput { get; private set; } public bool DockedMode { get; set; } @@ -55,15 +58,18 @@ namespace Ryujinx.HLE.HOS.SystemState Profiles = new ConcurrentDictionary(); - UserId DefaultUuid = new UserId("00000000000000000000000000000001"); + UInt128 DefaultUuid = new UInt128("00000000000000000000000000000001"); AddUser(DefaultUuid, "Player"); + OpenUser(DefaultUuid); } public void SetLanguage(SystemLanguage Language) { DesiredLanguageCode = GetLanguageCode((int)Language); + + DesiredTitleLanguage = Enum.Parse(Enum.GetName(typeof(SystemLanguage), Language)); } public void SetAudioOutputAsTv() @@ -81,24 +87,24 @@ namespace Ryujinx.HLE.HOS.SystemState ActiveAudioOutput = AudioOutputs[2]; } - public void AddUser(UserId Uuid, string Name) + public void AddUser(UInt128 Uuid, string Name) { UserProfile Profile = new UserProfile(Uuid, Name); - Profiles.AddOrUpdate(Uuid.UserIdHex, Profile, (Key, Old) => Profile); + Profiles.AddOrUpdate(Uuid.ToString(), Profile, (Key, Old) => Profile); } - public void OpenUser(UserId Uuid) + public void OpenUser(UInt128 Uuid) { - if (Profiles.TryGetValue(Uuid.UserIdHex, out UserProfile Profile)) + if (Profiles.TryGetValue(Uuid.ToString(), out UserProfile Profile)) { (LastOpenUser = Profile).AccountState = OpenCloseState.Open; } } - public void CloseUser(UserId Uuid) + public void CloseUser(UInt128 Uuid) { - if (Profiles.TryGetValue(Uuid.UserIdHex, out UserProfile Profile)) + if (Profiles.TryGetValue(Uuid.ToString(), out UserProfile Profile)) { Profile.AccountState = OpenCloseState.Closed; } @@ -109,9 +115,9 @@ namespace Ryujinx.HLE.HOS.SystemState return Profiles.Count; } - internal bool TryGetUser(UserId Uuid, out UserProfile Profile) + internal bool TryGetUser(UInt128 Uuid, out UserProfile Profile) { - return Profiles.TryGetValue(Uuid.UserIdHex, out Profile); + return Profiles.TryGetValue(Uuid.ToString(), out Profile); } internal IEnumerable GetAllUsers() diff --git a/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs b/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs new file mode 100644 index 0000000000..f481ac2932 --- /dev/null +++ b/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.HLE.HOS.SystemState +{ + public enum TitleLanguage + { + AmericanEnglish, + BritishEnglish, + Japanese, + French, + German, + LatinAmericanSpanish, + Spanish, + Italian, + Dutch, + CanadianFrench, + Portuguese, + Russian, + Korean, + Taiwanese, + Chinese + } +} diff --git a/Ryujinx.HLE/HOS/SystemState/UserId.cs b/Ryujinx.HLE/HOS/SystemState/UserId.cs deleted file mode 100644 index 1e7c53dd00..0000000000 --- a/Ryujinx.HLE/HOS/SystemState/UserId.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Ryujinx.HLE.Utilities; -using System; -using System.IO; -using System.Linq; - -namespace Ryujinx.HLE.HOS.SystemState -{ - public struct UserId - { - public string UserIdHex { get; private set; } - - public byte[] Bytes { get; private set; } - - public UserId(long Low, long High) - { - if ((Low | High) == 0) - { - throw new ArgumentException("Zero is not a valid user id!"); - } - - byte[] Bytes = new byte[16]; - - int Index = Bytes.Length; - - void WriteBytes(long Value) - { - for (int Byte = 0; Byte < 8; Byte++) - { - Bytes[--Index] = (byte)(Value >> Byte * 8); - } - } - - WriteBytes(Low); - WriteBytes(High); - - UserIdHex = string.Empty; - - foreach (byte Byte in Bytes) - { - UserIdHex += Byte.ToString("X2"); - } - - this.Bytes = Bytes; - } - - public UserId(string UserIdHex) - { - if (UserIdHex == null || UserIdHex.Length != 32 || !UserIdHex.All("0123456789abcdefABCDEF".Contains)) - { - throw new ArgumentException("Invalid user id!", nameof(UserIdHex)); - } - - if (UserIdHex == "00000000000000000000000000000000") - { - throw new ArgumentException("Zero is not a valid user id!", nameof(UserIdHex)); - } - - this.UserIdHex = UserIdHex.ToUpper(); - - Bytes = StringUtils.HexToBytes(UserIdHex); - } - - internal void Write(BinaryWriter Writer) - { - for (int Index = Bytes.Length - 1; Index >= 0; Index--) - { - Writer.Write(Bytes[Index]); - } - } - - public override string ToString() - { - return UserIdHex; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/SystemState/UserProfile.cs b/Ryujinx.HLE/HOS/SystemState/UserProfile.cs index 63852cdf4f..e08bc48aa3 100644 --- a/Ryujinx.HLE/HOS/SystemState/UserProfile.cs +++ b/Ryujinx.HLE/HOS/SystemState/UserProfile.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.HLE.Utilities; +using System; namespace Ryujinx.HLE.HOS.SystemState { @@ -6,7 +7,7 @@ namespace Ryujinx.HLE.HOS.SystemState { private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - public UserId Uuid { get; private set; } + public UInt128 Uuid { get; private set; } public string Name { get; private set; } @@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.SystemState public OpenCloseState AccountState { get; set; } public OpenCloseState OnlinePlayState { get; set; } - public UserProfile(UserId Uuid, string Name) + public UserProfile(UInt128 Uuid, string Name) { this.Uuid = Uuid; this.Name = Name; diff --git a/Ryujinx.HLE/Hid/Hid.cs b/Ryujinx.HLE/Hid/Hid.cs index 0353b3baec..21580223e7 100644 --- a/Ryujinx.HLE/Hid/Hid.cs +++ b/Ryujinx.HLE/Hid/Hid.cs @@ -1,4 +1,5 @@ -using Ryujinx.HLE.HOS; +using Ryujinx.Common; +using Ryujinx.HLE.HOS; using System; namespace Ryujinx.HLE.Input @@ -98,12 +99,12 @@ namespace Ryujinx.HLE.Input HidControllerColorDesc SplitColorDesc = 0; - Device.Memory.WriteInt32(BaseControllerOffset + 0x0, (int)Type); + Device.Memory.WriteInt32(BaseControllerOffset + 0x00, (int)Type); - Device.Memory.WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); + Device.Memory.WriteInt32(BaseControllerOffset + 0x04, IsHalf ? 1 : 0); - Device.Memory.WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc); - Device.Memory.WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody); + Device.Memory.WriteInt32(BaseControllerOffset + 0x08, (int)SingleColorDesc); + Device.Memory.WriteInt32(BaseControllerOffset + 0x0c, (int)SingleColorBody); Device.Memory.WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons); Device.Memory.WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc); @@ -114,6 +115,55 @@ namespace Ryujinx.HLE.Input Device.Memory.WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons); } + private HidControllerButtons UpdateStickButtons( + HidJoystickPosition LeftStick, + HidJoystickPosition RightStick) + { + HidControllerButtons Result = 0; + + if (RightStick.DX < 0) + { + Result |= HidControllerButtons.KEY_RSTICK_LEFT; + } + + if (RightStick.DX > 0) + { + Result |= HidControllerButtons.KEY_RSTICK_RIGHT; + } + + if (RightStick.DY < 0) + { + Result |= HidControllerButtons.KEY_RSTICK_DOWN; + } + + if (RightStick.DY > 0) + { + Result |= HidControllerButtons.KEY_RSTICK_UP; + } + + if (LeftStick.DX < 0) + { + Result |= HidControllerButtons.KEY_LSTICK_LEFT; + } + + if (LeftStick.DX > 0) + { + Result |= HidControllerButtons.KEY_LSTICK_RIGHT; + } + + if (LeftStick.DY < 0) + { + Result |= HidControllerButtons.KEY_LSTICK_DOWN; + } + + if (LeftStick.DY > 0) + { + Result |= HidControllerButtons.KEY_LSTICK_UP; + } + + return Result; + } + public void SetJoyconButton( HidControllerId ControllerId, HidControllerLayouts ControllerLayout, @@ -121,6 +171,8 @@ namespace Ryujinx.HLE.Input HidJoystickPosition LeftStick, HidJoystickPosition RightStick) { + Buttons |= UpdateStickButtons(LeftStick, RightStick); + long ControllerOffset = HidPosition + HidControllersOffset; ControllerOffset += (int)ControllerId * HidControllerSize; @@ -135,8 +187,8 @@ namespace Ryujinx.HLE.Input long Timestamp = GetTimestamp(); - Device.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp); - Device.Memory.WriteInt64(ControllerOffset + 0x8, HidEntryCount); + Device.Memory.WriteInt64(ControllerOffset + 0x00, Timestamp); + Device.Memory.WriteInt64(ControllerOffset + 0x08, HidEntryCount); Device.Memory.WriteInt64(ControllerOffset + 0x10, CurrEntry); Device.Memory.WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); @@ -148,8 +200,8 @@ namespace Ryujinx.HLE.Input long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1; - Device.Memory.WriteInt64(ControllerOffset + 0x0, SampleCounter); - Device.Memory.WriteInt64(ControllerOffset + 0x8, SampleCounter); + Device.Memory.WriteInt64(ControllerOffset + 0x00, SampleCounter); + Device.Memory.WriteInt64(ControllerOffset + 0x08, SampleCounter); Device.Memory.WriteInt64(ControllerOffset + 0x10, (uint)Buttons); @@ -174,8 +226,8 @@ namespace Ryujinx.HLE.Input long Timestamp = GetTimestamp(); - Device.Memory.WriteInt64(TouchScreenOffset + 0x0, Timestamp); - Device.Memory.WriteInt64(TouchScreenOffset + 0x8, HidEntryCount); + Device.Memory.WriteInt64(TouchScreenOffset + 0x00, Timestamp); + Device.Memory.WriteInt64(TouchScreenOffset + 0x08, HidEntryCount); Device.Memory.WriteInt64(TouchScreenOffset + 0x10, CurrEntry); Device.Memory.WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1); Device.Memory.WriteInt64(TouchScreenOffset + 0x20, Timestamp); @@ -188,8 +240,8 @@ namespace Ryujinx.HLE.Input TouchEntryOffset += CurrEntry * HidTouchEntrySize; - Device.Memory.WriteInt64(TouchEntryOffset + 0x0, SampleCounter); - Device.Memory.WriteInt64(TouchEntryOffset + 0x8, Points.Length); + Device.Memory.WriteInt64(TouchEntryOffset + 0x00, SampleCounter); + Device.Memory.WriteInt64(TouchEntryOffset + 0x08, Points.Length); TouchEntryOffset += HidTouchEntryHeaderSize; @@ -199,9 +251,9 @@ namespace Ryujinx.HLE.Input foreach (HidTouchPoint Point in Points) { - Device.Memory.WriteInt64(TouchEntryOffset + 0x0, Timestamp); - Device.Memory.WriteInt32(TouchEntryOffset + 0x8, Padding); - Device.Memory.WriteInt32(TouchEntryOffset + 0xc, Index++); + Device.Memory.WriteInt64(TouchEntryOffset + 0x00, Timestamp); + Device.Memory.WriteInt32(TouchEntryOffset + 0x08, Padding); + Device.Memory.WriteInt32(TouchEntryOffset + 0x0c, Index++); Device.Memory.WriteInt32(TouchEntryOffset + 0x10, Point.X); Device.Memory.WriteInt32(TouchEntryOffset + 0x14, Point.Y); Device.Memory.WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); @@ -215,7 +267,7 @@ namespace Ryujinx.HLE.Input private static long GetTimestamp() { - return (long)((ulong)Environment.TickCount * 19_200); + return PerformanceCounter.ElapsedMilliseconds * 19200; } } } diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index 6a3f0b9735..d4d79073ec 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -3,18 +3,21 @@ using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Utilities; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; +using System.Linq; namespace Ryujinx.HLE.Loaders { class Executable { + private MemoryManager Memory; + private List Dynamic; - private Dictionary m_SymbolTable; - - public IReadOnlyDictionary SymbolTable => m_SymbolTable; + public ReadOnlyCollection SymbolTable; public string Name { get; private set; } @@ -23,16 +26,12 @@ namespace Ryujinx.HLE.Loaders public long ImageBase { get; private set; } public long ImageEnd { get; private set; } - private AMemory Memory; - private KMemoryManager MemoryManager; - public Executable(IExecutable Exe, KMemoryManager MemoryManager, AMemory Memory, long ImageBase) + public Executable(IExecutable Exe, KMemoryManager MemoryManager, MemoryManager Memory, long ImageBase) { Dynamic = new List(); - m_SymbolTable = new Dictionary(); - FilePath = Exe.FilePath; if (FilePath != null) @@ -50,21 +49,49 @@ namespace Ryujinx.HLE.Loaders long DataPosition = ImageBase + (uint)Exe.DataOffset; long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize); - long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize); + long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize); long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize); + long BssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize); - long DataAndBssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize) + DataSize; + long DataAndBssSize = BssSize + DataSize; ImageEnd = DataPosition + DataAndBssSize; - MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize); + if (Exe.SourceAddress == 0) + { + MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize); - MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); - MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite); + MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); + MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite); - Memory.WriteBytes(TextPosition, Exe.Text); - Memory.WriteBytes(ROPosition, Exe.RO); - Memory.WriteBytes(DataPosition, Exe.Data); + Memory.WriteBytes(TextPosition, Exe.Text); + Memory.WriteBytes(ROPosition, Exe.RO); + Memory.WriteBytes(DataPosition, Exe.Data); + } + else + { + long Result = MemoryManager.MapProcessCodeMemory(TextPosition, Exe.SourceAddress, TextSize + ROSize + DataSize); + + if (Result != 0) + { + throw new InvalidOperationException(); + } + + MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); + MemoryManager.SetProcessMemoryPermission(DataPosition, DataSize, MemoryPermission.ReadAndWrite); + + if (Exe.BssAddress != 0 && Exe.BssSize != 0) + { + Result = MemoryManager.MapProcessCodeMemory(DataPosition + DataSize, Exe.BssAddress, BssSize); + + if (Result != 0) + { + throw new InvalidOperationException(); + } + + MemoryManager.SetProcessMemoryPermission(DataPosition + DataSize, BssSize, MemoryPermission.ReadAndWrite); + } + } if (Exe.Mod0Offset == 0) { @@ -103,14 +130,18 @@ namespace Ryujinx.HLE.Loaders long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT); + List Symbols = new List(); + while ((ulong)SymTblAddr < (ulong)StrTblAddr) { ElfSym Sym = GetSymbol(SymTblAddr, StrTblAddr); - m_SymbolTable.TryAdd(Sym.Value, Sym.Name); + Symbols.Add(Sym); SymTblAddr += SymEntSize; } + + SymbolTable = Array.AsReadOnly(Symbols.OrderBy(x => x.Value).ToArray()); } private ElfRel GetRelocation(long Position) diff --git a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs index 44bad61497..6f0952abdb 100644 --- a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs +++ b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs @@ -8,6 +8,9 @@ namespace Ryujinx.HLE.Loaders.Executables byte[] RO { get; } byte[] Data { get; } + long SourceAddress { get; } + long BssAddress { get; } + int Mod0Offset { get; } int TextOffset { get; } int ROOffset { get; } diff --git a/Ryujinx.HLE/Loaders/Executables/Nro.cs b/Ryujinx.HLE/Loaders/Executables/Nro.cs index 0b5068d7b9..6015da2132 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nro.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nro.cs @@ -16,9 +16,14 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } - public Nro(Stream Input, string FilePath) + public long SourceAddress { get; private set; } + public long BssAddress { get; private set; } + + public Nro(Stream Input, string FilePath, long SourceAddress = 0, long BssAddress = 0) { - this.FilePath = FilePath; + this.FilePath = FilePath; + this.SourceAddress = SourceAddress; + this.BssAddress = BssAddress; BinaryReader Reader = new BinaryReader(Input); diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/Nso.cs index fef9c4b853..c7b48a5f35 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nso.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nso.cs @@ -18,6 +18,9 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } + public long SourceAddress { get; private set; } + public long BssAddress { get; private set; } + [Flags] private enum NsoFlags { @@ -33,6 +36,9 @@ namespace Ryujinx.HLE.Loaders.Executables { this.FilePath = FilePath; + SourceAddress = 0; + BssAddress = 0; + BinaryReader Reader = new BinaryReader(Input); Input.Seek(0, SeekOrigin.Begin); diff --git a/Ryujinx.HLE/Memory/ArenaAllocator.cs b/Ryujinx.HLE/Memory/ArenaAllocator.cs index 5e15f46a51..9bcb7873e0 100644 --- a/Ryujinx.HLE/Memory/ArenaAllocator.cs +++ b/Ryujinx.HLE/Memory/ArenaAllocator.cs @@ -45,6 +45,33 @@ namespace Ryujinx.HLE.Memory Rg.Position += Size; Rg.Size -= Size; + if (Rg.Size == 0) + { + //Region is empty, just remove it. + FreeRegions.Remove(Node); + } + else if (Node.Previous != null) + { + //Re-sort based on size (smaller first). + Node = Node.Previous; + + FreeRegions.Remove(Node.Next); + + while (Node != null && (ulong)Node.Value.Size > (ulong)Rg.Size) + { + Node = Node.Previous; + } + + if (Node != null) + { + FreeRegions.AddAfter(Node, Rg); + } + else + { + FreeRegions.AddFirst(Rg); + } + } + TotalUsedSize += Size; return true; @@ -65,7 +92,7 @@ namespace Ryujinx.HLE.Memory Region NewRg = new Region(Position, Size); LinkedListNode Node = FreeRegions.First; - LinkedListNode PrevSz = Node; + LinkedListNode PrevSz = null; while (Node != null) { @@ -77,27 +104,38 @@ namespace Ryujinx.HLE.Memory if (Rg.Position == End) { + //Current region position matches the end of the freed region, + //just merge the two and remove the current region from the list. NewRg.Size += Rg.Size; FreeRegions.Remove(Node); } else if (RgEnd == Position) { + //End of the current region matches the position of the freed region, + //just merge the two and remove the current region from the list. NewRg.Position = Rg.Position; NewRg.Size += Rg.Size; FreeRegions.Remove(Node); } - else if ((ulong)Rg.Size < (ulong)NewRg.Size && - (ulong)Rg.Size > (ulong)PrevSz.Value.Size) + else { - PrevSz = Node; + if (PrevSz == null) + { + PrevSz = Node; + } + else if ((ulong)Rg.Size < (ulong)NewRg.Size && + (ulong)Rg.Size > (ulong)PrevSz.Value.Size) + { + PrevSz = Node; + } } Node = NextNode; } - if ((ulong)PrevSz.Value.Size < (ulong)Size) + if (PrevSz != null && (ulong)PrevSz.Value.Size < (ulong)Size) { FreeRegions.AddAfter(PrevSz, NewRg); } diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index cd1bb03458..71a0cf1c47 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -24,8 +24,13 @@ + - + + + + + diff --git a/Ryujinx.HLE/RyujinxProfileImage.jpg b/Ryujinx.HLE/RyujinxProfileImage.jpg index fe9ec2a9590d2d53ef1dcffc8078f5bd04e7b00f..55e4c43cf2c110b7a9fd17177e0056b15f0ac47e 100644 GIT binary patch literal 52969 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 diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index a3f874ee59..8de49ca4a8 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gal; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.Input; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Memory; using System; using System.Threading; @@ -15,8 +14,6 @@ namespace Ryujinx.HLE { internal IAalOutput AudioOut { get; private set; } - public Logger Log { get; private set; } - internal DeviceMemory Memory { get; private set; } internal NvGpu Gpu { get; private set; } @@ -49,8 +46,6 @@ namespace Ryujinx.HLE this.AudioOut = AudioOut; - Log = new Logger(); - Memory = new DeviceMemory(); Gpu = new NvGpu(Renderer); @@ -93,12 +88,12 @@ namespace Ryujinx.HLE public bool WaitFifo() { - return Gpu.Fifo.Event.WaitOne(8); + return Gpu.Pusher.WaitForCommands(); } public void ProcessFrame() { - Gpu.Fifo.DispatchCalls(); + Gpu.Pusher.DispatchCalls(); } internal void Unload() diff --git a/Ryujinx.HLE/Utilities/LinuxError.cs b/Ryujinx.HLE/Utilities/LinuxError.cs new file mode 100644 index 0000000000..5c322f83cf --- /dev/null +++ b/Ryujinx.HLE/Utilities/LinuxError.cs @@ -0,0 +1,152 @@ +namespace Ryujinx.HLE.Utilities +{ + enum LinuxError + { + SUCCESS = 0, + EPERM = 1 /* Operation not permitted */, + ENOENT = 2 /* No such file or directory */, + ESRCH = 3 /* No such process */, + EINTR = 4 /* Interrupted system call */, + EIO = 5 /* I/O error */, + ENXIO = 6 /* No such device or address */, + E2BIG = 7 /* Argument list too long */, + ENOEXEC = 8 /* Exec format error */, + EBADF = 9 /* Bad file number */, + ECHILD = 10 /* No child processes */, + EAGAIN = 11 /* Try again */, + ENOMEM = 12 /* Out of memory */, + EACCES = 13 /* Permission denied */, + EFAULT = 14 /* Bad address */, + ENOTBLK = 15 /* Block device required */, + EBUSY = 16 /* Device or resource busy */, + EEXIST = 17 /* File exists */, + EXDEV = 18 /* Cross-device link */, + ENODEV = 19 /* No such device */, + ENOTDIR = 20 /* Not a directory */, + EISDIR = 21 /* Is a directory */, + EINVAL = 22 /* Invalid argument */, + ENFILE = 23 /* File table overflow */, + EMFILE = 24 /* Too many open files */, + ENOTTY = 25 /* Not a typewriter */, + ETXTBSY = 26 /* Text file busy */, + EFBIG = 27 /* File too large */, + ENOSPC = 28 /* No space left on device */, + ESPIPE = 29 /* Illegal seek */, + EROFS = 30 /* Read-only file system */, + EMLINK = 31 /* Too many links */, + EPIPE = 32 /* Broken pipe */, + EDOM = 33 /* Math argument out of domain of func */, + ERANGE = 34 /* Math result not representable */, + EDEADLK = 35 /* Resource deadlock would occur */, + ENAMETOOLONG = 36 /* File name too long */, + ENOLCK = 37 /* No record locks available */, + + /* + * This error code is special: arch syscall entry code will return + * -ENOSYS if users try to call a syscall that doesn't exist. To keep + * failures of syscalls that really do exist distinguishable from + * failures due to attempts to use a nonexistent syscall, syscall + * implementations should refrain from returning -ENOSYS. + */ + ENOSYS = 38 /* Invalid system call number */, + ENOTEMPTY = 39 /* Directory not empty */, + ELOOP = 40 /* Too many symbolic links encountered */, + EWOULDBLOCK = EAGAIN /* Operation would block */, + ENOMSG = 42 /* No message of desired type */, + EIDRM = 43 /* Identifier removed */, + ECHRNG = 44 /* Channel number out of range */, + EL2NSYNC = 45 /* Level 2 not synchronized */, + EL3HLT = 46 /* Level 3 halted */, + EL3RST = 47 /* Level 3 reset */, + ELNRNG = 48 /* Link number out of range */, + EUNATCH = 49 /* Protocol driver not attached */, + ENOCSI = 50 /* No CSI structure available */, + EL2HLT = 51 /* Level 2 halted */, + EBADE = 52 /* Invalid exchange */, + EBADR = 53 /* Invalid request descriptor */, + EXFULL = 54 /* Exchange full */, + ENOANO = 55 /* No anode */, + EBADRQC = 56 /* Invalid request code */, + EBADSLT = 57 /* Invalid slot */, + EDEADLOCK = EDEADLK, + EBFONT = 59 /* Bad font file format */, + ENOSTR = 60 /* Device not a stream */, + ENODATA = 61 /* No data available */, + ETIME = 62 /* Timer expired */, + ENOSR = 63 /* Out of streams resources */, + ENONET = 64 /* Machine is not on the network */, + ENOPKG = 65 /* Package not installed */, + EREMOTE = 66 /* Object is remote */, + ENOLINK = 67 /* Link has been severed */, + EADV = 68 /* Advertise error */, + ESRMNT = 69 /* Srmount error */, + ECOMM = 70 /* Communication error on send */, + EPROTO = 71 /* Protocol error */, + EMULTIHOP = 72 /* Multihop attempted */, + EDOTDOT = 73 /* RFS specific error */, + EBADMSG = 74 /* Not a data message */, + EOVERFLOW = 75 /* Value too large for defined data type */, + ENOTUNIQ = 76 /* Name not unique on network */, + EBADFD = 77 /* File descriptor in bad state */, + EREMCHG = 78 /* Remote address changed */, + ELIBACC = 79 /* Can not access a needed shared library */, + ELIBBAD = 80 /* Accessing a corrupted shared library */, + ELIBSCN = 81 /* .lib section in a.out corrupted */, + ELIBMAX = 82 /* Attempting to link in too many shared libraries */, + ELIBEXEC = 83 /* Cannot exec a shared library directly */, + EILSEQ = 84 /* Illegal byte sequence */, + ERESTART = 85 /* Interrupted system call should be restarted */, + ESTRPIPE = 86 /* Streams pipe error */, + EUSERS = 87 /* Too many users */, + ENOTSOCK = 88 /* Socket operation on non-socket */, + EDESTADDRREQ = 89 /* Destination address required */, + EMSGSIZE = 90 /* Message too long */, + EPROTOTYPE = 91 /* Protocol wrong type for socket */, + ENOPROTOOPT = 92 /* Protocol not available */, + EPROTONOSUPPORT = 93 /* Protocol not supported */, + ESOCKTNOSUPPORT = 94 /* Socket type not supported */, + EOPNOTSUPP = 95 /* Operation not supported on transport endpoint */, + EPFNOSUPPORT = 96 /* Protocol family not supported */, + EAFNOSUPPORT = 97 /* Address family not supported by protocol */, + EADDRINUSE = 98 /* Address already in use */, + EADDRNOTAVAIL = 99 /* Cannot assign requested address */, + ENETDOWN = 100 /* Network is down */, + ENETUNREACH = 101 /* Network is unreachable */, + ENETRESET = 102 /* Network dropped connection because of reset */, + ECONNABORTED = 103 /* Software caused connection abort */, + ECONNRESET = 104 /* Connection reset by peer */, + ENOBUFS = 105 /* No buffer space available */, + EISCONN = 106 /* Transport endpoint is already connected */, + ENOTCONN = 107 /* Transport endpoint is not connected */, + ESHUTDOWN = 108 /* Cannot send after transport endpoint shutdown */, + ETOOMANYREFS = 109 /* Too many references: cannot splice */, + ETIMEDOUT = 110 /* Connection timed out */, + ECONNREFUSED = 111 /* Connection refused */, + EHOSTDOWN = 112 /* Host is down */, + EHOSTUNREACH = 113 /* No route to host */, + EALREADY = 114 /* Operation already in progress */, + EINPROGRESS = 115 /* Operation now in progress */, + ESTALE = 116 /* Stale file handle */, + EUCLEAN = 117 /* Structure needs cleaning */, + ENOTNAM = 118 /* Not a XENIX named type file */, + ENAVAIL = 119 /* No XENIX semaphores available */, + EISNAM = 120 /* Is a named type file */, + EREMOTEIO = 121 /* Remote I/O error */, + EDQUOT = 122 /* Quota exceeded */, + ENOMEDIUM = 123 /* No medium found */, + EMEDIUMTYPE = 124 /* Wrong medium type */, + ECANCELED = 125 /* Operation Canceled */, + ENOKEY = 126 /* Required key not available */, + EKEYEXPIRED = 127 /* Key has expired */, + EKEYREVOKED = 128 /* Key has been revoked */, + EKEYREJECTED = 129 /* Key was rejected by service */, + + /* for robust mutexes */ + EOWNERDEAD = 130 /* Owner died */, + ENOTRECOVERABLE = 131 /* State not recoverable */, + + ERFKILL = 132 /* Operation not possible due to RF-kill */, + + EHWPOISON = 133 /* Memory page has hardware error */, + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Utilities/StructReader.cs b/Ryujinx.HLE/Utilities/StructReader.cs index 6e942a9b0d..19fd267475 100644 --- a/Ryujinx.HLE/Utilities/StructReader.cs +++ b/Ryujinx.HLE/Utilities/StructReader.cs @@ -5,11 +5,11 @@ namespace Ryujinx.HLE.Utilities { class StructReader { - private AMemory Memory; + private MemoryManager Memory; public long Position { get; private set; } - public StructReader(AMemory Memory, long Position) + public StructReader(MemoryManager Memory, long Position) { this.Memory = Memory; this.Position = Position; @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.Utilities public T Read() where T : struct { - T Value = AMemoryHelper.Read(Memory, Position); + T Value = MemoryHelper.Read(Memory, Position); Position += Marshal.SizeOf(); @@ -34,7 +34,7 @@ namespace Ryujinx.HLE.Utilities for (int Index = 0; Index < Count; Index++) { - Output[Index] = AMemoryHelper.Read(Memory, Position); + Output[Index] = MemoryHelper.Read(Memory, Position); Position += StructSize; } diff --git a/Ryujinx.HLE/Utilities/StructWriter.cs b/Ryujinx.HLE/Utilities/StructWriter.cs index 8b88105c6a..a537e7a418 100644 --- a/Ryujinx.HLE/Utilities/StructWriter.cs +++ b/Ryujinx.HLE/Utilities/StructWriter.cs @@ -5,11 +5,11 @@ namespace Ryujinx.HLE.Utilities { class StructWriter { - private AMemory Memory; + private MemoryManager Memory; public long Position { get; private set; } - public StructWriter(AMemory Memory, long Position) + public StructWriter(MemoryManager Memory, long Position) { this.Memory = Memory; this.Position = Position; @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.Utilities public void Write(T Value) where T : struct { - AMemoryHelper.Write(Memory, Position, Value); + MemoryHelper.Write(Memory, Position, Value); Position += Marshal.SizeOf(); } diff --git a/Ryujinx.HLE/Utilities/UInt128.cs b/Ryujinx.HLE/Utilities/UInt128.cs new file mode 100644 index 0000000000..14e04e4a6a --- /dev/null +++ b/Ryujinx.HLE/Utilities/UInt128.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; +using System.Linq; + +namespace Ryujinx.HLE.Utilities +{ + public struct UInt128 + { + public long High { get; private set; } + public long Low { get; private set; } + + public UInt128(long Low, long High) + { + this.Low = Low; + this.High = High; + } + + public UInt128(string UInt128Hex) + { + if (UInt128Hex == null || UInt128Hex.Length != 32 || !UInt128Hex.All("0123456789abcdefABCDEF".Contains)) + { + throw new ArgumentException("Invalid Hex value!", nameof(UInt128Hex)); + } + + Low = Convert.ToInt64(UInt128Hex.Substring(16), 16); + High = Convert.ToInt64(UInt128Hex.Substring(0, 16), 16); + } + + public void Write(BinaryWriter BinaryWriter) + { + BinaryWriter.Write(Low); + BinaryWriter.Write(High); + } + + public override string ToString() + { + return High.ToString("x16") + Low.ToString("x16"); + } + + public bool IsZero() + { + return (Low | High) == 0; + } + } +} diff --git a/Ryujinx.HLE/Utilities/WSAError.cs b/Ryujinx.HLE/Utilities/WSAError.cs new file mode 100644 index 0000000000..55c04f2284 --- /dev/null +++ b/Ryujinx.HLE/Utilities/WSAError.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.HLE.Utilities +{ + public enum WSAError + { + /* + * All Windows Sockets error constants are biased by WSABASEERR from + * the "normal" + */ + WSABASEERR = 10000, + + /* + * Windows Sockets definitions of regular Microsoft C error constants + */ + WSAEINTR = (WSABASEERR + 4), + WSAEBADF = (WSABASEERR + 9), + WSAEACCES = (WSABASEERR + 13), + WSAEFAULT = (WSABASEERR + 14), + WSAEINVAL = (WSABASEERR + 22), + WSAEMFILE = (WSABASEERR + 24), + + /* + * Windows Sockets definitions of regular Berkeley error constants + */ + WSAEWOULDBLOCK = (WSABASEERR + 35), + WSAEINPROGRESS = (WSABASEERR + 36), + WSAEALREADY = (WSABASEERR + 37), + WSAENOTSOCK = (WSABASEERR + 38), + WSAEDESTADDRREQ = (WSABASEERR + 39), + WSAEMSGSIZE = (WSABASEERR + 40), + WSAEPROTOTYPE = (WSABASEERR + 41), + WSAENOPROTOOPT = (WSABASEERR + 42), + WSAEPROTONOSUPPORT = (WSABASEERR + 43), + WSAESOCKTNOSUPPORT = (WSABASEERR + 44), + WSAEOPNOTSUPP = (WSABASEERR + 45), + WSAEPFNOSUPPORT = (WSABASEERR + 46), + WSAEAFNOSUPPORT = (WSABASEERR + 47), + WSAEADDRINUSE = (WSABASEERR + 48), + WSAEADDRNOTAVAIL = (WSABASEERR + 49), + WSAENETDOWN = (WSABASEERR + 50), + WSAENETUNREACH = (WSABASEERR + 51), + WSAENETRESET = (WSABASEERR + 52), + WSAECONNABORTED = (WSABASEERR + 53), + WSAECONNRESET = (WSABASEERR + 54), + WSAENOBUFS = (WSABASEERR + 55), + WSAEISCONN = (WSABASEERR + 56), + WSAENOTCONN = (WSABASEERR + 57), + WSAESHUTDOWN = (WSABASEERR + 58), + WSAETOOMANYREFS = (WSABASEERR + 59), + WSAETIMEDOUT = (WSABASEERR + 60), + WSAECONNREFUSED = (WSABASEERR + 61), + WSAELOOP = (WSABASEERR + 62), + WSAENAMETOOLONG = (WSABASEERR + 63), + WSAEHOSTDOWN = (WSABASEERR + 64), + WSAEHOSTUNREACH = (WSABASEERR + 65), + WSAENOTEMPTY = (WSABASEERR + 66), + WSAEPROCLIM = (WSABASEERR + 67), + WSAEUSERS = (WSABASEERR + 68), + WSAEDQUOT = (WSABASEERR + 69), + WSAESTALE = (WSABASEERR + 70), + WSAEREMOTE = (WSABASEERR + 71), + + /* + * Extended Windows Sockets error constant definitions + */ + WSASYSNOTREADY = (WSABASEERR + 91), + WSAVERNOTSUPPORTED = (WSABASEERR + 92), + WSANOTINITIALISED = (WSABASEERR + 93), + WSAEDISCON = (WSABASEERR + 101), + WSAENOMORE = (WSABASEERR + 102), + WSAECANCELLED = (WSABASEERR + 103), + WSAEINVALIDPROCTABLE = (WSABASEERR + 104), + WSAEINVALIDPROVIDER = (WSABASEERR + 105), + WSAEPROVIDERFAILEDINIT = (WSABASEERR + 106), + WSASYSCALLFAILURE = (WSABASEERR + 107), + WSASERVICE_NOT_FOUND = (WSABASEERR + 108), + WSATYPE_NOT_FOUND = (WSABASEERR + 109), + WSA_E_NO_MORE = (WSABASEERR + 110), + WSA_E_CANCELLED = (WSABASEERR + 111), + WSAEREFUSED = (WSABASEERR + 112), + + /* + * Error return codes from gethostbyname() and gethostbyaddr() + * (when using the resolver). Note that these errors are + * retrieved via WSAGetLastError() and must therefore follow + * the rules for avoiding clashes with error numbers from + * specific implementations or language run-time systems. + * For this reason the codes are based at WSABASEERR+1001. + * Note also that [WSA]NO_ADDRESS is defined only for + * compatibility purposes. + */ + + /* Authoritative Answer: Host not found */ + WSAHOST_NOT_FOUND = (WSABASEERR + 1001), + + /* Non-Authoritative: Host not found, or SERVERFAIL */ + WSATRY_AGAIN = (WSABASEERR + 1002), + + /* Non-recoverable errors, FORMERR, REFUSED, NOTIMP */ + WSANO_RECOVERY = (WSABASEERR + 1003), + + /* Valid name, no data record of requested type */ + WSANO_DATA = (WSABASEERR + 1004), + + /* + * Define QOS related error return codes + * + */ + WSA_QOS_RECEIVERS = (WSABASEERR + 1005), + /* at least one Reserve has arrived */ + WSA_QOS_SENDERS = (WSABASEERR + 1006), + /* at least one Path has arrived */ + WSA_QOS_NO_SENDERS = (WSABASEERR + 1007), + /* there are no senders */ + WSA_QOS_NO_RECEIVERS = (WSABASEERR + 1008), + /* there are no receivers */ + WSA_QOS_REQUEST_CONFIRMED = (WSABASEERR + 1009), + /* Reserve has been confirmed */ + WSA_QOS_ADMISSION_FAILURE = (WSABASEERR + 1010), + /* error due to lack of resources */ + WSA_QOS_POLICY_FAILURE = (WSABASEERR + 1011), + /* rejected for administrative reasons - bad credentials */ + WSA_QOS_BAD_STYLE = (WSABASEERR + 1012), + /* unknown or conflicting style */ + WSA_QOS_BAD_OBJECT = (WSABASEERR + 1013), + /* problem with some part of the filterspec or providerspecific + * buffer in general */ + WSA_QOS_TRAFFIC_CTRL_ERROR = (WSABASEERR + 1014), + /* problem with some part of the flowspec */ + WSA_QOS_GENERIC_ERROR = (WSABASEERR + 1015), + } +} diff --git a/Ryujinx.Tests.Unicorn/MemoryPermission.cs b/Ryujinx.Tests.Unicorn/MemoryPermission.cs index d79b2adda1..a14c4e9cf1 100644 --- a/Ryujinx.Tests.Unicorn/MemoryPermission.cs +++ b/Ryujinx.Tests.Unicorn/MemoryPermission.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn { public enum MemoryPermission diff --git a/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs index 3554480c13..cf110598eb 100644 --- a/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs +++ b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn.Native { public enum ArmRegister diff --git a/Ryujinx.Tests.Unicorn/Native/Interface.cs b/Ryujinx.Tests.Unicorn/Native/Interface.cs index a6563220fe..b2786d1436 100644 --- a/Ryujinx.Tests.Unicorn/Native/Interface.cs +++ b/Ryujinx.Tests.Unicorn/Native/Interface.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.InteropServices; -using Ryujinx.Tests.Unicorn; namespace Ryujinx.Tests.Unicorn.Native { diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs index be088366da..73710faa89 100644 --- a/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs +++ b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn.Native { public enum UnicornArch diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs index 950583bdc0..a5040518dd 100644 --- a/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs +++ b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn.Native { public enum UnicornMode diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs index 1cd3671fe1..8ee4e76db9 100644 --- a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs +++ b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics.Contracts; -using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; diff --git a/Ryujinx.Tests.Unicorn/UnicornError.cs b/Ryujinx.Tests.Unicorn/UnicornError.cs index db56c615d1..85833ea008 100644 --- a/Ryujinx.Tests.Unicorn/UnicornError.cs +++ b/Ryujinx.Tests.Unicorn/UnicornError.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn { public enum UnicornError diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 1f7151ffe7..b970e05540 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -18,23 +18,23 @@ namespace Ryujinx.Tests.Cpu public class CpuTest { protected long Position { get; private set; } - private long Size; + private long _size; - private long EntryPoint; + private long _entryPoint; - private IntPtr RamPointer; + private IntPtr _ramPointer; - private AMemory Memory; - private AThread Thread; + private MemoryManager _memory; + private CpuThread _thread; - private static bool UnicornAvailable; - private UnicornAArch64 UnicornEmu; + private static bool _unicornAvailable; + private UnicornAArch64 _unicornEmu; static CpuTest() { - UnicornAvailable = UnicornAArch64.IsAvailable(); + _unicornAvailable = UnicornAArch64.IsAvailable(); - if (!UnicornAvailable) + if (!_unicornAvailable) { Console.WriteLine("WARNING: Could not find Unicorn."); } @@ -44,31 +44,31 @@ namespace Ryujinx.Tests.Cpu public void Setup() { Position = 0x1000; - Size = 0x1000; + _size = 0x1000; - EntryPoint = Position; + _entryPoint = Position; - ATranslator Translator = new ATranslator(); - RamPointer = Marshal.AllocHGlobal(new IntPtr(Size)); - Memory = new AMemory(RamPointer); - Memory.Map(Position, 0, Size); - Thread = new AThread(Translator, Memory, EntryPoint); + Translator translator = new Translator(); + _ramPointer = Marshal.AllocHGlobal(new IntPtr(_size)); + _memory = new MemoryManager(_ramPointer); + _memory.Map(Position, 0, _size); + _thread = new CpuThread(translator, _memory, _entryPoint); - if (UnicornAvailable) + if (_unicornAvailable) { - UnicornEmu = new UnicornAArch64(); - UnicornEmu.MemoryMap((ulong)Position, (ulong)Size, MemoryPermission.READ | MemoryPermission.EXEC); - UnicornEmu.PC = (ulong)EntryPoint; + _unicornEmu = new UnicornAArch64(); + _unicornEmu.MemoryMap((ulong)Position, (ulong)_size, MemoryPermission.READ | MemoryPermission.EXEC); + _unicornEmu.PC = (ulong)_entryPoint; } } [TearDown] public void Teardown() { - Marshal.FreeHGlobal(RamPointer); - Memory = null; - Thread = null; - UnicornEmu = null; + Marshal.FreeHGlobal(_ramPointer); + _memory = null; + _thread = null; + _unicornEmu = null; } protected void Reset() @@ -77,219 +77,367 @@ namespace Ryujinx.Tests.Cpu Setup(); } - protected void Opcode(uint Opcode) + protected void Opcode(uint opcode) { - Thread.Memory.WriteUInt32(Position, Opcode); + _thread.Memory.WriteUInt32(Position, opcode); - if (UnicornAvailable) + if (_unicornAvailable) { - UnicornEmu.MemoryWrite32((ulong)Position, Opcode); + _unicornEmu.MemoryWrite32((ulong)Position, opcode); } Position += 4; } - protected void SetThreadState(ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0, - Vector128 V0 = default(Vector128), - Vector128 V1 = default(Vector128), - Vector128 V2 = default(Vector128), - bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, - int Fpcr = 0x0, int Fpsr = 0x0) + protected void SetThreadState(ulong x0 = 0, ulong x1 = 0, ulong x2 = 0, ulong x3 = 0, ulong x31 = 0, + Vector128 v0 = default(Vector128), + Vector128 v1 = default(Vector128), + Vector128 v2 = default(Vector128), + Vector128 v3 = default(Vector128), + bool overflow = false, bool carry = false, bool zero = false, bool negative = false, + int fpcr = 0x0, int fpsr = 0x0) { - Thread.ThreadState.X0 = X0; - Thread.ThreadState.X1 = X1; - Thread.ThreadState.X2 = X2; - Thread.ThreadState.X3 = X3; + _thread.ThreadState.X0 = x0; + _thread.ThreadState.X1 = x1; + _thread.ThreadState.X2 = x2; + _thread.ThreadState.X3 = x3; - Thread.ThreadState.X31 = X31; + _thread.ThreadState.X31 = x31; - Thread.ThreadState.V0 = V0; - Thread.ThreadState.V1 = V1; - Thread.ThreadState.V2 = V2; + _thread.ThreadState.V0 = v0; + _thread.ThreadState.V1 = v1; + _thread.ThreadState.V2 = v2; + _thread.ThreadState.V3 = v3; - Thread.ThreadState.Overflow = Overflow; - Thread.ThreadState.Carry = Carry; - Thread.ThreadState.Zero = Zero; - Thread.ThreadState.Negative = Negative; + _thread.ThreadState.Overflow = overflow; + _thread.ThreadState.Carry = carry; + _thread.ThreadState.Zero = zero; + _thread.ThreadState.Negative = negative; - Thread.ThreadState.Fpcr = Fpcr; - Thread.ThreadState.Fpsr = Fpsr; + _thread.ThreadState.Fpcr = fpcr; + _thread.ThreadState.Fpsr = fpsr; - if (UnicornAvailable) + if (_unicornAvailable) { - UnicornEmu.X[0] = X0; - UnicornEmu.X[1] = X1; - UnicornEmu.X[2] = X2; - UnicornEmu.X[3] = X3; + _unicornEmu.X[0] = x0; + _unicornEmu.X[1] = x1; + _unicornEmu.X[2] = x2; + _unicornEmu.X[3] = x3; - UnicornEmu.SP = X31; + _unicornEmu.SP = x31; - UnicornEmu.Q[0] = V0; - UnicornEmu.Q[1] = V1; - UnicornEmu.Q[2] = V2; + _unicornEmu.Q[0] = v0; + _unicornEmu.Q[1] = v1; + _unicornEmu.Q[2] = v2; + _unicornEmu.Q[3] = v3; - UnicornEmu.OverflowFlag = Overflow; - UnicornEmu.CarryFlag = Carry; - UnicornEmu.ZeroFlag = Zero; - UnicornEmu.NegativeFlag = Negative; + _unicornEmu.OverflowFlag = overflow; + _unicornEmu.CarryFlag = carry; + _unicornEmu.ZeroFlag = zero; + _unicornEmu.NegativeFlag = negative; - UnicornEmu.Fpcr = Fpcr; - UnicornEmu.Fpsr = Fpsr; + _unicornEmu.Fpcr = fpcr; + _unicornEmu.Fpsr = fpsr; } } protected void ExecuteOpcodes() { - using (ManualResetEvent Wait = new ManualResetEvent(false)) + using (ManualResetEvent wait = new ManualResetEvent(false)) { - Thread.ThreadState.Break += (sender, e) => Thread.StopExecution(); - Thread.WorkFinished += (sender, e) => Wait.Set(); + _thread.ThreadState.Break += (sender, e) => _thread.StopExecution(); + _thread.WorkFinished += (sender, e) => wait.Set(); - Thread.Execute(); - Wait.WaitOne(); + _thread.Execute(); + wait.WaitOne(); } - if (UnicornAvailable) + if (_unicornAvailable) { - UnicornEmu.RunForCount((ulong)(Position - EntryPoint - 8) / 4); + _unicornEmu.RunForCount((ulong)(Position - _entryPoint - 8) / 4); } } - protected AThreadState GetThreadState() => Thread.ThreadState; + protected CpuThreadState GetThreadState() => _thread.ThreadState; - protected AThreadState SingleOpcode(uint Opcode, - ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0, - Vector128 V0 = default(Vector128), - Vector128 V1 = default(Vector128), - Vector128 V2 = default(Vector128), - bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, - int Fpcr = 0x0, int Fpsr = 0x0) + protected CpuThreadState SingleOpcode(uint opcode, + ulong x0 = 0, ulong x1 = 0, ulong x2 = 0, ulong x3 = 0, ulong x31 = 0, + Vector128 v0 = default(Vector128), + Vector128 v1 = default(Vector128), + Vector128 v2 = default(Vector128), + Vector128 v3 = default(Vector128), + bool overflow = false, bool carry = false, bool zero = false, bool negative = false, + int fpcr = 0x0, int fpsr = 0x0) { - this.Opcode(Opcode); - this.Opcode(0xD4200000); // BRK #0 - this.Opcode(0xD65F03C0); // RET - SetThreadState(X0, X1, X2, X3, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr, Fpsr); + Opcode(opcode); + Opcode(0xD4200000); // BRK #0 + Opcode(0xD65F03C0); // RET + SetThreadState(x0, x1, x2, x3, x31, v0, v1, v2, v3, overflow, carry, zero, negative, fpcr, fpsr); ExecuteOpcodes(); return GetThreadState(); } - [Flags] - protected enum FPSR + /// Rounding Mode control field. + public enum RMode + { + /// Round to Nearest mode. + Rn, + /// Round towards Plus Infinity mode. + Rp, + /// Round towards Minus Infinity mode. + Rm, + /// Round towards Zero mode. + Rz + }; + + /// Floating-point Control Register. + protected enum Fpcr + { + /// Rounding Mode control field. + RMode = 22, + /// Flush-to-zero mode control bit. + Fz = 24, + /// Default NaN mode control bit. + Dn = 25, + /// Alternative half-precision control bit. + Ahp = 26 + } + + /// Floating-point Status Register. + [Flags] protected enum Fpsr { None = 0, /// Invalid Operation cumulative floating-point exception bit. - IOC = 1 << 0, + Ioc = 1 << 0, /// Divide by Zero cumulative floating-point exception bit. - DZC = 1 << 1, + Dzc = 1 << 1, /// Overflow cumulative floating-point exception bit. - OFC = 1 << 2, + Ofc = 1 << 2, /// Underflow cumulative floating-point exception bit. - UFC = 1 << 3, + Ufc = 1 << 3, /// Inexact cumulative floating-point exception bit. - IXC = 1 << 4, + Ixc = 1 << 4, /// Input Denormal cumulative floating-point exception bit. - IDC = 1 << 7, + Idc = 1 << 7, + /// Cumulative saturation bit. - QC = 1 << 27 + Qc = 1 << 27 } - protected void CompareAgainstUnicorn(FPSR FpsrMask = FPSR.None) + [Flags] protected enum FpSkips { - if (!UnicornAvailable) + None = 0, + + IfNaNS = 1, + IfNaND = 2, + + IfUnderflow = 4, + IfOverflow = 8 + } + + protected enum FpTolerances + { + None, + + UpToOneUlpsS, + UpToOneUlpsD + } + + protected void CompareAgainstUnicorn( + Fpsr fpsrMask = Fpsr.None, + FpSkips fpSkips = FpSkips.None, + FpTolerances fpTolerances = FpTolerances.None) + { + if (!_unicornAvailable) { return; } - Assert.That(Thread.ThreadState.X0, Is.EqualTo(UnicornEmu.X[0])); - Assert.That(Thread.ThreadState.X1, Is.EqualTo(UnicornEmu.X[1])); - Assert.That(Thread.ThreadState.X2, Is.EqualTo(UnicornEmu.X[2])); - Assert.That(Thread.ThreadState.X3, Is.EqualTo(UnicornEmu.X[3])); - Assert.That(Thread.ThreadState.X4, Is.EqualTo(UnicornEmu.X[4])); - Assert.That(Thread.ThreadState.X5, Is.EqualTo(UnicornEmu.X[5])); - Assert.That(Thread.ThreadState.X6, Is.EqualTo(UnicornEmu.X[6])); - Assert.That(Thread.ThreadState.X7, Is.EqualTo(UnicornEmu.X[7])); - Assert.That(Thread.ThreadState.X8, Is.EqualTo(UnicornEmu.X[8])); - Assert.That(Thread.ThreadState.X9, Is.EqualTo(UnicornEmu.X[9])); - Assert.That(Thread.ThreadState.X10, Is.EqualTo(UnicornEmu.X[10])); - Assert.That(Thread.ThreadState.X11, Is.EqualTo(UnicornEmu.X[11])); - Assert.That(Thread.ThreadState.X12, Is.EqualTo(UnicornEmu.X[12])); - Assert.That(Thread.ThreadState.X13, Is.EqualTo(UnicornEmu.X[13])); - Assert.That(Thread.ThreadState.X14, Is.EqualTo(UnicornEmu.X[14])); - Assert.That(Thread.ThreadState.X15, Is.EqualTo(UnicornEmu.X[15])); - Assert.That(Thread.ThreadState.X16, Is.EqualTo(UnicornEmu.X[16])); - Assert.That(Thread.ThreadState.X17, Is.EqualTo(UnicornEmu.X[17])); - Assert.That(Thread.ThreadState.X18, Is.EqualTo(UnicornEmu.X[18])); - Assert.That(Thread.ThreadState.X19, Is.EqualTo(UnicornEmu.X[19])); - Assert.That(Thread.ThreadState.X20, Is.EqualTo(UnicornEmu.X[20])); - Assert.That(Thread.ThreadState.X21, Is.EqualTo(UnicornEmu.X[21])); - Assert.That(Thread.ThreadState.X22, Is.EqualTo(UnicornEmu.X[22])); - Assert.That(Thread.ThreadState.X23, Is.EqualTo(UnicornEmu.X[23])); - Assert.That(Thread.ThreadState.X24, Is.EqualTo(UnicornEmu.X[24])); - Assert.That(Thread.ThreadState.X25, Is.EqualTo(UnicornEmu.X[25])); - Assert.That(Thread.ThreadState.X26, Is.EqualTo(UnicornEmu.X[26])); - Assert.That(Thread.ThreadState.X27, Is.EqualTo(UnicornEmu.X[27])); - Assert.That(Thread.ThreadState.X28, Is.EqualTo(UnicornEmu.X[28])); - Assert.That(Thread.ThreadState.X29, Is.EqualTo(UnicornEmu.X[29])); - Assert.That(Thread.ThreadState.X30, Is.EqualTo(UnicornEmu.X[30])); + if (fpSkips != FpSkips.None) + { + ManageFpSkips(fpSkips); + } - Assert.That(Thread.ThreadState.X31, Is.EqualTo(UnicornEmu.SP)); + Assert.That(_thread.ThreadState.X0, Is.EqualTo(_unicornEmu.X[0])); + Assert.That(_thread.ThreadState.X1, Is.EqualTo(_unicornEmu.X[1])); + Assert.That(_thread.ThreadState.X2, Is.EqualTo(_unicornEmu.X[2])); + Assert.That(_thread.ThreadState.X3, Is.EqualTo(_unicornEmu.X[3])); + Assert.That(_thread.ThreadState.X4, Is.EqualTo(_unicornEmu.X[4])); + Assert.That(_thread.ThreadState.X5, Is.EqualTo(_unicornEmu.X[5])); + Assert.That(_thread.ThreadState.X6, Is.EqualTo(_unicornEmu.X[6])); + Assert.That(_thread.ThreadState.X7, Is.EqualTo(_unicornEmu.X[7])); + Assert.That(_thread.ThreadState.X8, Is.EqualTo(_unicornEmu.X[8])); + Assert.That(_thread.ThreadState.X9, Is.EqualTo(_unicornEmu.X[9])); + Assert.That(_thread.ThreadState.X10, Is.EqualTo(_unicornEmu.X[10])); + Assert.That(_thread.ThreadState.X11, Is.EqualTo(_unicornEmu.X[11])); + Assert.That(_thread.ThreadState.X12, Is.EqualTo(_unicornEmu.X[12])); + Assert.That(_thread.ThreadState.X13, Is.EqualTo(_unicornEmu.X[13])); + Assert.That(_thread.ThreadState.X14, Is.EqualTo(_unicornEmu.X[14])); + Assert.That(_thread.ThreadState.X15, Is.EqualTo(_unicornEmu.X[15])); + Assert.That(_thread.ThreadState.X16, Is.EqualTo(_unicornEmu.X[16])); + Assert.That(_thread.ThreadState.X17, Is.EqualTo(_unicornEmu.X[17])); + Assert.That(_thread.ThreadState.X18, Is.EqualTo(_unicornEmu.X[18])); + Assert.That(_thread.ThreadState.X19, Is.EqualTo(_unicornEmu.X[19])); + Assert.That(_thread.ThreadState.X20, Is.EqualTo(_unicornEmu.X[20])); + Assert.That(_thread.ThreadState.X21, Is.EqualTo(_unicornEmu.X[21])); + Assert.That(_thread.ThreadState.X22, Is.EqualTo(_unicornEmu.X[22])); + Assert.That(_thread.ThreadState.X23, Is.EqualTo(_unicornEmu.X[23])); + Assert.That(_thread.ThreadState.X24, Is.EqualTo(_unicornEmu.X[24])); + Assert.That(_thread.ThreadState.X25, Is.EqualTo(_unicornEmu.X[25])); + Assert.That(_thread.ThreadState.X26, Is.EqualTo(_unicornEmu.X[26])); + Assert.That(_thread.ThreadState.X27, Is.EqualTo(_unicornEmu.X[27])); + Assert.That(_thread.ThreadState.X28, Is.EqualTo(_unicornEmu.X[28])); + Assert.That(_thread.ThreadState.X29, Is.EqualTo(_unicornEmu.X[29])); + Assert.That(_thread.ThreadState.X30, Is.EqualTo(_unicornEmu.X[30])); - Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0])); - Assert.That(Thread.ThreadState.V1, Is.EqualTo(UnicornEmu.Q[1])); - Assert.That(Thread.ThreadState.V2, Is.EqualTo(UnicornEmu.Q[2])); - Assert.That(Thread.ThreadState.V3, Is.EqualTo(UnicornEmu.Q[3])); - Assert.That(Thread.ThreadState.V4, Is.EqualTo(UnicornEmu.Q[4])); - Assert.That(Thread.ThreadState.V5, Is.EqualTo(UnicornEmu.Q[5])); - Assert.That(Thread.ThreadState.V6, Is.EqualTo(UnicornEmu.Q[6])); - Assert.That(Thread.ThreadState.V7, Is.EqualTo(UnicornEmu.Q[7])); - Assert.That(Thread.ThreadState.V8, Is.EqualTo(UnicornEmu.Q[8])); - Assert.That(Thread.ThreadState.V9, Is.EqualTo(UnicornEmu.Q[9])); - Assert.That(Thread.ThreadState.V10, Is.EqualTo(UnicornEmu.Q[10])); - Assert.That(Thread.ThreadState.V11, Is.EqualTo(UnicornEmu.Q[11])); - Assert.That(Thread.ThreadState.V12, Is.EqualTo(UnicornEmu.Q[12])); - Assert.That(Thread.ThreadState.V13, Is.EqualTo(UnicornEmu.Q[13])); - Assert.That(Thread.ThreadState.V14, Is.EqualTo(UnicornEmu.Q[14])); - Assert.That(Thread.ThreadState.V15, Is.EqualTo(UnicornEmu.Q[15])); - Assert.That(Thread.ThreadState.V16, Is.EqualTo(UnicornEmu.Q[16])); - Assert.That(Thread.ThreadState.V17, Is.EqualTo(UnicornEmu.Q[17])); - Assert.That(Thread.ThreadState.V18, Is.EqualTo(UnicornEmu.Q[18])); - Assert.That(Thread.ThreadState.V19, Is.EqualTo(UnicornEmu.Q[19])); - Assert.That(Thread.ThreadState.V20, Is.EqualTo(UnicornEmu.Q[20])); - Assert.That(Thread.ThreadState.V21, Is.EqualTo(UnicornEmu.Q[21])); - Assert.That(Thread.ThreadState.V22, Is.EqualTo(UnicornEmu.Q[22])); - Assert.That(Thread.ThreadState.V23, Is.EqualTo(UnicornEmu.Q[23])); - Assert.That(Thread.ThreadState.V24, Is.EqualTo(UnicornEmu.Q[24])); - Assert.That(Thread.ThreadState.V25, Is.EqualTo(UnicornEmu.Q[25])); - Assert.That(Thread.ThreadState.V26, Is.EqualTo(UnicornEmu.Q[26])); - Assert.That(Thread.ThreadState.V27, Is.EqualTo(UnicornEmu.Q[27])); - Assert.That(Thread.ThreadState.V28, Is.EqualTo(UnicornEmu.Q[28])); - Assert.That(Thread.ThreadState.V29, Is.EqualTo(UnicornEmu.Q[29])); - Assert.That(Thread.ThreadState.V30, Is.EqualTo(UnicornEmu.Q[30])); - Assert.That(Thread.ThreadState.V31, Is.EqualTo(UnicornEmu.Q[31])); - Assert.That(Thread.ThreadState.V31, Is.EqualTo(UnicornEmu.Q[31])); + Assert.That(_thread.ThreadState.X31, Is.EqualTo(_unicornEmu.SP)); - Assert.That(Thread.ThreadState.Fpcr, Is.EqualTo(UnicornEmu.Fpcr)); - Assert.That(Thread.ThreadState.Fpsr & (int)FpsrMask, Is.EqualTo(UnicornEmu.Fpsr & (int)FpsrMask)); + if (fpTolerances == FpTolerances.None) + { + Assert.That(_thread.ThreadState.V0, Is.EqualTo(_unicornEmu.Q[0])); + } + else + { + ManageFpTolerances(fpTolerances); + } + Assert.That(_thread.ThreadState.V1, Is.EqualTo(_unicornEmu.Q[1])); + Assert.That(_thread.ThreadState.V2, Is.EqualTo(_unicornEmu.Q[2])); + Assert.That(_thread.ThreadState.V3, Is.EqualTo(_unicornEmu.Q[3])); + Assert.That(_thread.ThreadState.V4, Is.EqualTo(_unicornEmu.Q[4])); + Assert.That(_thread.ThreadState.V5, Is.EqualTo(_unicornEmu.Q[5])); + Assert.That(_thread.ThreadState.V6, Is.EqualTo(_unicornEmu.Q[6])); + Assert.That(_thread.ThreadState.V7, Is.EqualTo(_unicornEmu.Q[7])); + Assert.That(_thread.ThreadState.V8, Is.EqualTo(_unicornEmu.Q[8])); + Assert.That(_thread.ThreadState.V9, Is.EqualTo(_unicornEmu.Q[9])); + Assert.That(_thread.ThreadState.V10, Is.EqualTo(_unicornEmu.Q[10])); + Assert.That(_thread.ThreadState.V11, Is.EqualTo(_unicornEmu.Q[11])); + Assert.That(_thread.ThreadState.V12, Is.EqualTo(_unicornEmu.Q[12])); + Assert.That(_thread.ThreadState.V13, Is.EqualTo(_unicornEmu.Q[13])); + Assert.That(_thread.ThreadState.V14, Is.EqualTo(_unicornEmu.Q[14])); + Assert.That(_thread.ThreadState.V15, Is.EqualTo(_unicornEmu.Q[15])); + Assert.That(_thread.ThreadState.V16, Is.EqualTo(_unicornEmu.Q[16])); + Assert.That(_thread.ThreadState.V17, Is.EqualTo(_unicornEmu.Q[17])); + Assert.That(_thread.ThreadState.V18, Is.EqualTo(_unicornEmu.Q[18])); + Assert.That(_thread.ThreadState.V19, Is.EqualTo(_unicornEmu.Q[19])); + Assert.That(_thread.ThreadState.V20, Is.EqualTo(_unicornEmu.Q[20])); + Assert.That(_thread.ThreadState.V21, Is.EqualTo(_unicornEmu.Q[21])); + Assert.That(_thread.ThreadState.V22, Is.EqualTo(_unicornEmu.Q[22])); + Assert.That(_thread.ThreadState.V23, Is.EqualTo(_unicornEmu.Q[23])); + Assert.That(_thread.ThreadState.V24, Is.EqualTo(_unicornEmu.Q[24])); + Assert.That(_thread.ThreadState.V25, Is.EqualTo(_unicornEmu.Q[25])); + Assert.That(_thread.ThreadState.V26, Is.EqualTo(_unicornEmu.Q[26])); + Assert.That(_thread.ThreadState.V27, Is.EqualTo(_unicornEmu.Q[27])); + Assert.That(_thread.ThreadState.V28, Is.EqualTo(_unicornEmu.Q[28])); + Assert.That(_thread.ThreadState.V29, Is.EqualTo(_unicornEmu.Q[29])); + Assert.That(_thread.ThreadState.V30, Is.EqualTo(_unicornEmu.Q[30])); + Assert.That(_thread.ThreadState.V31, Is.EqualTo(_unicornEmu.Q[31])); - Assert.That(Thread.ThreadState.Overflow, Is.EqualTo(UnicornEmu.OverflowFlag)); - Assert.That(Thread.ThreadState.Carry, Is.EqualTo(UnicornEmu.CarryFlag)); - Assert.That(Thread.ThreadState.Zero, Is.EqualTo(UnicornEmu.ZeroFlag)); - Assert.That(Thread.ThreadState.Negative, Is.EqualTo(UnicornEmu.NegativeFlag)); + Assert.That(_thread.ThreadState.Fpcr, Is.EqualTo(_unicornEmu.Fpcr)); + Assert.That(_thread.ThreadState.Fpsr & (int)fpsrMask, Is.EqualTo(_unicornEmu.Fpsr & (int)fpsrMask)); + + Assert.That(_thread.ThreadState.Overflow, Is.EqualTo(_unicornEmu.OverflowFlag)); + Assert.That(_thread.ThreadState.Carry, Is.EqualTo(_unicornEmu.CarryFlag)); + Assert.That(_thread.ThreadState.Zero, Is.EqualTo(_unicornEmu.ZeroFlag)); + Assert.That(_thread.ThreadState.Negative, Is.EqualTo(_unicornEmu.NegativeFlag)); } - protected static Vector128 MakeVectorE0(double E0) + private void ManageFpSkips(FpSkips fpSkips) + { + if (fpSkips.HasFlag(FpSkips.IfNaNS)) + { + if (float.IsNaN(VectorExtractSingle(_unicornEmu.Q[0], (byte)0))) + { + Assert.Ignore("NaN test."); + } + } + else if (fpSkips.HasFlag(FpSkips.IfNaND)) + { + if (double.IsNaN(VectorExtractDouble(_unicornEmu.Q[0], (byte)0))) + { + Assert.Ignore("NaN test."); + } + } + + if (fpSkips.HasFlag(FpSkips.IfUnderflow)) + { + if ((_unicornEmu.Fpsr & (int)Fpsr.Ufc) != 0) + { + Assert.Ignore("Underflow test."); + } + } + + if (fpSkips.HasFlag(FpSkips.IfOverflow)) + { + if ((_unicornEmu.Fpsr & (int)Fpsr.Ofc) != 0) + { + Assert.Ignore("Overflow test."); + } + } + } + + private void ManageFpTolerances(FpTolerances fpTolerances) + { + if (!Is.EqualTo(_unicornEmu.Q[0]).ApplyTo(_thread.ThreadState.V0).IsSuccess) + { + if (fpTolerances == FpTolerances.UpToOneUlpsS) + { + if (IsNormalOrSubnormalS(VectorExtractSingle(_unicornEmu.Q[0], (byte)0)) && + IsNormalOrSubnormalS(VectorExtractSingle(_thread.ThreadState.V0, (byte)0))) + { + Assert.That (VectorExtractSingle(_thread.ThreadState.V0, (byte)0), + Is.EqualTo(VectorExtractSingle(_unicornEmu.Q[0], (byte)0)).Within(1).Ulps); + Assert.That (VectorExtractSingle(_thread.ThreadState.V0, (byte)1), + Is.EqualTo(VectorExtractSingle(_unicornEmu.Q[0], (byte)1)).Within(1).Ulps); + Assert.That (VectorExtractSingle(_thread.ThreadState.V0, (byte)2), + Is.EqualTo(VectorExtractSingle(_unicornEmu.Q[0], (byte)2)).Within(1).Ulps); + Assert.That (VectorExtractSingle(_thread.ThreadState.V0, (byte)3), + Is.EqualTo(VectorExtractSingle(_unicornEmu.Q[0], (byte)3)).Within(1).Ulps); + + Console.WriteLine(fpTolerances); + } + else + { + Assert.That(_thread.ThreadState.V0, Is.EqualTo(_unicornEmu.Q[0])); + } + } + + if (fpTolerances == FpTolerances.UpToOneUlpsD) + { + if (IsNormalOrSubnormalD(VectorExtractDouble(_unicornEmu.Q[0], (byte)0)) && + IsNormalOrSubnormalD(VectorExtractDouble(_thread.ThreadState.V0, (byte)0))) + { + Assert.That (VectorExtractDouble(_thread.ThreadState.V0, (byte)0), + Is.EqualTo(VectorExtractDouble(_unicornEmu.Q[0], (byte)0)).Within(1).Ulps); + Assert.That (VectorExtractDouble(_thread.ThreadState.V0, (byte)1), + Is.EqualTo(VectorExtractDouble(_unicornEmu.Q[0], (byte)1)).Within(1).Ulps); + + Console.WriteLine(fpTolerances); + } + else + { + Assert.That(_thread.ThreadState.V0, Is.EqualTo(_unicornEmu.Q[0])); + } + } + } + + bool IsNormalOrSubnormalS(float f) => float.IsNormal(f) || float.IsSubnormal(f); + + bool IsNormalOrSubnormalD(double d) => double.IsNormal(d) || double.IsSubnormal(d); + } + + protected static Vector128 MakeVectorE0(double e0) { if (!Sse2.IsSupported) { throw new PlatformNotSupportedException(); } - return Sse.StaticCast(Sse2.SetVector128(0, BitConverter.DoubleToInt64Bits(E0))); + return Sse.StaticCast(Sse2.SetVector128(0, BitConverter.DoubleToInt64Bits(e0))); } - protected static Vector128 MakeVectorE0E1(double E0, double E1) + protected static Vector128 MakeVectorE0E1(double e0, double e1) { if (!Sse2.IsSupported) { @@ -297,79 +445,154 @@ namespace Ryujinx.Tests.Cpu } return Sse.StaticCast( - Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1), BitConverter.DoubleToInt64Bits(E0))); + Sse2.SetVector128(BitConverter.DoubleToInt64Bits(e1), BitConverter.DoubleToInt64Bits(e0))); } - protected static Vector128 MakeVectorE1(double E1) + protected static Vector128 MakeVectorE1(double e1) { if (!Sse2.IsSupported) { throw new PlatformNotSupportedException(); } - return Sse.StaticCast(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1), 0)); + return Sse.StaticCast(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(e1), 0)); } - protected static double VectorExtractDouble(Vector128 Vector, byte Index) + protected static float VectorExtractSingle(Vector128 vector, byte index) { if (!Sse41.IsSupported) { throw new PlatformNotSupportedException(); } - long Value = Sse41.Extract(Sse.StaticCast(Vector), Index); + int value = Sse41.Extract(Sse.StaticCast(vector), index); - return BitConverter.Int64BitsToDouble(Value); + return BitConverter.Int32BitsToSingle(value); } - protected static Vector128 MakeVectorE0(ulong E0) - { - if (!Sse2.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - return Sse.StaticCast(Sse2.SetVector128(0, E0)); - } - - protected static Vector128 MakeVectorE0E1(ulong E0, ulong E1) - { - if (!Sse2.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - return Sse.StaticCast(Sse2.SetVector128(E1, E0)); - } - - protected static Vector128 MakeVectorE1(ulong E1) - { - if (!Sse2.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - return Sse.StaticCast(Sse2.SetVector128(E1, 0)); - } - - protected static ulong GetVectorE0(Vector128 Vector) + protected static double VectorExtractDouble(Vector128 vector, byte index) { if (!Sse41.IsSupported) { throw new PlatformNotSupportedException(); } - return Sse41.Extract(Sse.StaticCast(Vector), (byte)0); + long value = Sse41.Extract(Sse.StaticCast(vector), index); + + return BitConverter.Int64BitsToDouble(value); } - protected static ulong GetVectorE1(Vector128 Vector) + protected static Vector128 MakeVectorE0(ulong e0) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + return Sse.StaticCast(Sse2.SetVector128(0, e0)); + } + + protected static Vector128 MakeVectorE0E1(ulong e0, ulong e1) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + return Sse.StaticCast(Sse2.SetVector128(e1, e0)); + } + + protected static Vector128 MakeVectorE1(ulong e1) + { + if (!Sse2.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + return Sse.StaticCast(Sse2.SetVector128(e1, 0)); + } + + protected static ulong GetVectorE0(Vector128 vector) { if (!Sse41.IsSupported) { throw new PlatformNotSupportedException(); } - return Sse41.Extract(Sse.StaticCast(Vector), (byte)1); + return Sse41.Extract(Sse.StaticCast(vector), (byte)0); + } + + protected static ulong GetVectorE1(Vector128 vector) + { + if (!Sse41.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + return Sse41.Extract(Sse.StaticCast(vector), (byte)1); + } + + protected static ushort GenNormalH() + { + uint rnd; + + do rnd = TestContext.CurrentContext.Random.NextUShort(); + while (( rnd & 0x7C00u) == 0u || + (~rnd & 0x7C00u) == 0u); + + return (ushort)rnd; + } + + protected static ushort GenSubnormalH() + { + uint rnd; + + do rnd = TestContext.CurrentContext.Random.NextUShort(); + while ((rnd & 0x03FFu) == 0u); + + return (ushort)(rnd & 0x83FFu); + } + + protected static uint GenNormalS() + { + uint rnd; + + do rnd = TestContext.CurrentContext.Random.NextUInt(); + while (( rnd & 0x7F800000u) == 0u || + (~rnd & 0x7F800000u) == 0u); + + return rnd; + } + + protected static uint GenSubnormalS() + { + uint rnd; + + do rnd = TestContext.CurrentContext.Random.NextUInt(); + while ((rnd & 0x007FFFFFu) == 0u); + + return rnd & 0x807FFFFFu; + } + + protected static ulong GenNormalD() + { + ulong rnd; + + do rnd = TestContext.CurrentContext.Random.NextULong(); + while (( rnd & 0x7FF0000000000000ul) == 0ul || + (~rnd & 0x7FF0000000000000ul) == 0ul); + + return rnd; + } + + protected static ulong GenSubnormalD() + { + ulong rnd; + + do rnd = TestContext.CurrentContext.Random.NextULong(); + while ((rnd & 0x000FFFFFFFFFFFFFul) == 0ul); + + return rnd & 0x800FFFFFFFFFFFFFul; } } } diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/Ryujinx.Tests/Cpu/CpuTestAlu.cs index 43952d195b..81fc265b73 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAlu.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAlu.cs @@ -1,189 +1,187 @@ #define Alu -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("Alu")] // Tested: second half of 2018. + [Category("Alu")] public sealed class CpuTestAlu : CpuTest { #if Alu private const int RndCnt = 2; [Test, Pairwise, Description("CLS , ")] - public void Cls_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Cls_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) { - uint Opcode = 0xDAC01400; // CLS X0, X0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xDAC01400; // CLS X0, X0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CLS , ")] - public void Cls_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Cls_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) { - uint Opcode = 0x5AC01400; // CLS W0, W0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5AC01400; // CLS W0, W0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CLZ , ")] - public void Clz_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Clz_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) { - uint Opcode = 0xDAC01000; // CLZ X0, X0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xDAC01000; // CLZ X0, X0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CLZ , ")] - public void Clz_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Clz_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) { - uint Opcode = 0x5AC01000; // CLZ W0, W0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5AC01000; // CLZ W0, W0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RBIT , ")] - public void Rbit_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Rbit_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) { - uint Opcode = 0xDAC00000; // RBIT X0, X0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xDAC00000; // RBIT X0, X0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RBIT , ")] - public void Rbit_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Rbit_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) { - uint Opcode = 0x5AC00000; // RBIT W0, W0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5AC00000; // RBIT W0, W0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV16 , ")] - public void Rev16_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Rev16_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) { - uint Opcode = 0xDAC00400; // REV16 X0, X0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xDAC00400; // REV16 X0, X0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV16 , ")] - public void Rev16_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Rev16_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) { - uint Opcode = 0x5AC00400; // REV16 W0, W0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5AC00400; // REV16 W0, W0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV32 , ")] - public void Rev32_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Rev32_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) { - uint Opcode = 0xDAC00800; // REV32 X0, X0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xDAC00800; // REV32 X0, X0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV , ")] - public void Rev32_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Rev32_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn) { - uint Opcode = 0x5AC00800; // REV W0, W0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5AC00800; // REV W0, W0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV64 , ")] - public void Rev64_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Rev64_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn) { - uint Opcode = 0xDAC00C00; // REV64 X0, X0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xDAC00C00; // REV64 X0, X0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestAluImm.cs b/Ryujinx.Tests/Cpu/CpuTestAluImm.cs index 61eadefd35..9551ce2ce4 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluImm.cs @@ -1,12 +1,10 @@ #define AluImm -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("AluImm")] // Tested: second half of 2018. + [Category("AluImm")] public sealed class CpuTestAluImm : CpuTest { #if AluImm @@ -16,436 +14,420 @@ namespace Ryujinx.Tests.Cpu private const int RndCntImmr = 2; [Test, Pairwise, Description("ADD , , #{, }")] - public void Add_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Add_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, [Values(0b00u, 0b01u)] uint shift) // { - uint Opcode = 0x91000000; // ADD X0, X0, #0, LSL #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); + uint opcode = 0x91000000; // ADD X0, X0, #0, LSL #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ThreadState = SingleOpcode(Opcode, X1: Xn_SP); + SingleOpcode(opcode, x1: xnSp); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP); + SingleOpcode(opcode, x31: xnSp); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , #{, }")] - public void Add_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Add_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, [Values(0b00u, 0b01u)] uint shift) // { - uint Opcode = 0x11000000; // ADD W0, W0, #0, LSL #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); + uint opcode = 0x11000000; // ADD W0, W0, #0, LSL #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP); + SingleOpcode(opcode, x31: wnWsp); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , #{, }")] - public void Adds_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Adds_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, [Values(0b00u, 0b01u)] uint shift) // { - uint Opcode = 0xB1000000; // ADDS X0, X0, #0, LSL #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); + uint opcode = 0xB1000000; // ADDS X0, X0, #0, LSL #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ThreadState = SingleOpcode(Opcode, X1: Xn_SP); + SingleOpcode(opcode, x1: xnSp); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP); + SingleOpcode(opcode, x31: xnSp); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , #{, }")] - public void Adds_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Adds_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, [Values(0b00u, 0b01u)] uint shift) // { - uint Opcode = 0x31000000; // ADDS W0, W0, #0, LSL #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); + uint opcode = 0x31000000; // ADDS W0, W0, #0, LSL #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP); + SingleOpcode(opcode, x31: wnWsp); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("AND , , #")] - public void And_N1_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void And_N1_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, RndCntImms)] uint imms, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr) // { - uint Opcode = 0x92400000; // AND X0, X0, #0x1 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x92400000; // AND X0, X0, #0x1 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("AND , , #")] - public void And_N0_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void And_N0_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // { - uint Opcode = 0x92000000; // AND X0, X0, #0x100000001 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x92000000; // AND X0, X0, #0x100000001 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("AND , , #")] - public void And_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void And_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // { - uint Opcode = 0x12000000; // AND W0, W0, #0x1 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x12000000; // AND W0, W0, #0x1 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ANDS , , #")] - public void Ands_N1_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Ands_N1_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, RndCntImms)] uint imms, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr) // { - uint Opcode = 0xF2400000; // ANDS X0, X0, #0x1 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0xF2400000; // ANDS X0, X0, #0x1 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ANDS , , #")] - public void Ands_N0_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Ands_N0_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // { - uint Opcode = 0xF2000000; // ANDS X0, X0, #0x100000001 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0xF2000000; // ANDS X0, X0, #0x100000001 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ANDS , , #")] - public void Ands_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Ands_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // { - uint Opcode = 0x72000000; // ANDS W0, W0, #0x1 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x72000000; // ANDS W0, W0, #0x1 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EOR , , #")] - public void Eor_N1_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Eor_N1_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, RndCntImms)] uint imms, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr) // { - uint Opcode = 0xD2400000; // EOR X0, X0, #0x1 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0xD2400000; // EOR X0, X0, #0x1 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EOR , , #")] - public void Eor_N0_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Eor_N0_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // { - uint Opcode = 0xD2000000; // EOR X0, X0, #0x100000001 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0xD2000000; // EOR X0, X0, #0x100000001 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EOR , , #")] - public void Eor_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Eor_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // { - uint Opcode = 0x52000000; // EOR W0, W0, #0x1 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x52000000; // EOR W0, W0, #0x1 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORR , , #")] - public void Orr_N1_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Orr_N1_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, RndCntImms)] uint imms, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr) // { - uint Opcode = 0xB2400000; // ORR X0, X0, #0x1 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0xB2400000; // ORR X0, X0, #0x1 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORR , , #")] - public void Orr_N0_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Orr_N0_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // { - uint Opcode = 0xB2000000; // ORR X0, X0, #0x100000001 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0xB2000000; // ORR X0, X0, #0x100000001 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORR , , #")] - public void Orr_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Orr_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, RndCntImms)] uint imms, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr) // { - uint Opcode = 0x32000000; // ORR W0, W0, #0x1 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x32000000; // ORR W0, W0, #0x1 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , #{, }")] - public void Sub_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Sub_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, [Values(0b00u, 0b01u)] uint shift) // { - uint Opcode = 0xD1000000; // SUB X0, X0, #0, LSL #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); + uint opcode = 0xD1000000; // SUB X0, X0, #0, LSL #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ThreadState = SingleOpcode(Opcode, X1: Xn_SP); + SingleOpcode(opcode, x1: xnSp); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP); + SingleOpcode(opcode, x31: xnSp); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , #{, }")] - public void Sub_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Sub_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, [Values(0b00u, 0b01u)] uint shift) // { - uint Opcode = 0x51000000; // SUB W0, W0, #0, LSL #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); + uint opcode = 0x51000000; // SUB W0, W0, #0, LSL #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP); + SingleOpcode(opcode, x31: wnWsp); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , #{, }")] - public void Subs_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Subs_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, [Values(0b00u, 0b01u)] uint shift) // { - uint Opcode = 0xF1000000; // SUBS X0, X0, #0, LSL #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); + uint opcode = 0xF1000000; // SUBS X0, X0, #0, LSL #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ThreadState = SingleOpcode(Opcode, X1: Xn_SP); + SingleOpcode(opcode, x1: xnSp); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP); + SingleOpcode(opcode, x31: xnSp); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , #{, }")] - public void Subs_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Subs_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, [Values(0b00u, 0b01u)] uint shift) // { - uint Opcode = 0x71000000; // SUBS W0, W0, #0, LSL #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); + uint opcode = 0x71000000; // SUBS W0, W0, #0, LSL #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP); + SingleOpcode(opcode, x31: wnWsp); } CompareAgainstUnicorn(); diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRs.cs b/Ryujinx.Tests/Cpu/CpuTestAluRs.cs index 58a99dfb18..2d4013e2fb 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluRs.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluRs.cs @@ -1,12 +1,10 @@ #define AluRs -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("AluRs")] // Tested: second half of 2018. + [Category("AluRs")] public sealed class CpuTestAluRs : CpuTest { #if AluRs @@ -15,1107 +13,1107 @@ namespace Ryujinx.Tests.Cpu private const int RndCntLsb = 2; [Test, Pairwise, Description("ADC , , ")] - public void Adc_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adc_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, - [Values] bool CarryIn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + [Values] bool carryIn) { - uint Opcode = 0x9A000000; // ADC X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9A000000; // ADC X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31, Carry: CarryIn); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31, carry: carryIn); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADC , , ")] - public void Adc_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adc_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, - [Values] bool CarryIn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + [Values] bool carryIn) { - uint Opcode = 0x1A000000; // ADC W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1A000000; // ADC W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31, Carry: CarryIn); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31, carry: carryIn); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADCS , , ")] - public void Adcs_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adcs_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, - [Values] bool CarryIn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + [Values] bool carryIn) { - uint Opcode = 0xBA000000; // ADCS X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xBA000000; // ADCS X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31, Carry: CarryIn); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31, carry: carryIn); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADCS , , ")] - public void Adcs_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adcs_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, - [Values] bool CarryIn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + [Values] bool carryIn) { - uint Opcode = 0x3A000000; // ADCS W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x3A000000; // ADCS W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31, Carry: CarryIn); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31, carry: carryIn); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , {, #}")] - public void Add_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0x8B000000; // ADD X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x8B000000; // ADD X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , {, #}")] - public void Add_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x0B000000; // ADD W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x0B000000; // ADD W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, #}")] - public void Adds_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xAB000000; // ADDS X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xAB000000; // ADDS X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, #}")] - public void Adds_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x2B000000; // ADDS W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x2B000000; // ADDS W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("AND , , {, #}")] - public void And_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void And_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0x8A000000; // AND X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x8A000000; // AND X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("AND , , {, #}")] - public void And_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void And_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x0A000000; // AND W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x0A000000; // AND W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ANDS , , {, #}")] - public void Ands_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Ands_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xEA000000; // ANDS X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xEA000000; // ANDS X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ANDS , , {, #}")] - public void Ands_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Ands_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x6A000000; // ANDS W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x6A000000; // ANDS W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ASRV , , ")] - public void Asrv_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Asrv_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9AC02800; // ASRV X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9AC02800; // ASRV X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ASRV , , ")] - public void Asrv_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Asrv_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) { - uint Opcode = 0x1AC02800; // ASRV W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC02800; // ASRV W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BIC , , {, #}")] - public void Bic_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Bic_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0x8A200000; // BIC X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x8A200000; // BIC X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BIC , , {, #}")] - public void Bic_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Bic_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x0A200000; // BIC W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x0A200000; // BIC W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BICS , , {, #}")] - public void Bics_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Bics_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xEA200000; // BICS X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xEA200000; // BICS X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BICS , , {, #}")] - public void Bics_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Bics_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x6A200000; // BICS W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x6A200000; // BICS W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CRC32X , , "), Ignore("Unicorn fails.")] - public void Crc32x([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + public void Crc32x([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values((ulong)0x00_00_00_00_00_00_00_00, (ulong)0x7F_FF_FF_FF_FF_FF_FF_FF, (ulong)0x80_00_00_00_00_00_00_00, - (ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(RndCnt)] ulong Xm) + (ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9AC04C00; // CRC32X W0, W0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9AC04C00; // CRC32X W0, W0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Xm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: xm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CRC32W , , "), Ignore("Unicorn fails.")] - public void Crc32w([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + public void Crc32w([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF, - (uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(RndCnt)] uint Wm) + (uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(RndCnt)] uint wm) { - uint Opcode = 0x1AC04800; // CRC32W W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC04800; // CRC32W W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CRC32H , , "), Ignore("Unicorn fails.")] - public void Crc32h([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + public void Crc32h([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values((ushort)0x00_00, (ushort)0x7F_FF, - (ushort)0x80_00, (ushort)0xFF_FF)] [Random(RndCnt)] ushort Wm) + (ushort)0x80_00, (ushort)0xFF_FF)] [Random(RndCnt)] ushort wm) { - uint Opcode = 0x1AC04400; // CRC32H W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC04400; // CRC32H W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CRC32B , , "), Ignore("Unicorn fails.")] - public void Crc32b([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + public void Crc32b([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm) + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm) { - uint Opcode = 0x1AC04000; // CRC32B W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC04000; // CRC32B W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CRC32CX , , ")] - public void Crc32cx([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + public void Crc32cx([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values((ulong)0x00_00_00_00_00_00_00_00, (ulong)0x7F_FF_FF_FF_FF_FF_FF_FF, (ulong)0x80_00_00_00_00_00_00_00, - (ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(RndCnt)] ulong Xm) + (ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9AC05C00; // CRC32CX W0, W0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9AC05C00; // CRC32CX W0, W0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Xm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: xm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CRC32CW , , ")] - public void Crc32cw([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + public void Crc32cw([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF, - (uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(RndCnt)] uint Wm) + (uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(RndCnt)] uint wm) { - uint Opcode = 0x1AC05800; // CRC32CW W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC05800; // CRC32CW W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CRC32CH , , ")] - public void Crc32ch([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + public void Crc32ch([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values((ushort)0x00_00, (ushort)0x7F_FF, - (ushort)0x80_00, (ushort)0xFF_FF)] [Random(RndCnt)] ushort Wm) + (ushort)0x80_00, (ushort)0xFF_FF)] [Random(RndCnt)] ushort wm) { - uint Opcode = 0x1AC05400; // CRC32CH W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC05400; // CRC32CH W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CRC32CB , , ")] - public void Crc32cb([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + public void Crc32cb([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(0x00000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm) + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm) { - uint Opcode = 0x1AC05000; // CRC32CB W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC05000; // CRC32CB W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EON , , {, #}")] - public void Eon_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Eon_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xCA200000; // EON X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xCA200000; // EON X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EON , , {, #}")] - public void Eon_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Eon_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x4A200000; // EON W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x4A200000; // EON W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EOR , , {, #}")] - public void Eor_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Eor_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xCA000000; // EOR X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xCA000000; // EOR X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EOR , , {, #}")] - public void Eor_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Eor_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x4A000000; // EOR W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x4A000000; // EOR W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EXTR , , , #")] - public void Extr_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Extr_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntLsb)] uint lsb) { - uint Opcode = 0x93C00000; // EXTR X0, X0, X0, #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((lsb & 63) << 10); + uint opcode = 0x93C00000; // EXTR X0, X0, X0, #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((lsb & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EXTR , , , #")] - public void Extr_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Extr_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntLsb)] uint lsb) { - uint Opcode = 0x13800000; // EXTR W0, W0, W0, #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((lsb & 63) << 10); + uint opcode = 0x13800000; // EXTR W0, W0, W0, #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((lsb & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("LSLV , , ")] - public void Lslv_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Lslv_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9AC02000; // LSLV X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9AC02000; // LSLV X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("LSLV , , ")] - public void Lslv_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Lslv_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) { - uint Opcode = 0x1AC02000; // LSLV W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC02000; // LSLV W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("LSRV , , ")] - public void Lsrv_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Lsrv_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9AC02400; // LSRV X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9AC02400; // LSRV X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("LSRV , , ")] - public void Lsrv_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Lsrv_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) { - uint Opcode = 0x1AC02400; // LSRV W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC02400; // LSRV W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORN , , {, #}")] - public void Orn_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Orn_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xAA200000; // ORN X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xAA200000; // ORN X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORN , , {, #}")] - public void Orn_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Orn_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x2A200000; // ORN W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x2A200000; // ORN W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORR , , {, #}")] - public void Orr_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Orr_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xAA000000; // ORR X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xAA000000; // ORR X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORR , , {, #}")] - public void Orr_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Orr_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x2A000000; // ORR W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x2A000000; // ORR W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RORV , , ")] - public void Rorv_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Rorv_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9AC02C00; // RORV X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9AC02C00; // RORV X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RORV , , ")] - public void Rorv_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Rorv_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) { - uint Opcode = 0x1AC02C00; // RORV W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC02C00; // RORV W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SBC , , ")] - public void Sbc_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sbc_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, - [Values] bool CarryIn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + [Values] bool carryIn) { - uint Opcode = 0xDA000000; // SBC X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xDA000000; // SBC X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31, Carry: CarryIn); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31, carry: carryIn); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SBC , , ")] - public void Sbc_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sbc_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, - [Values] bool CarryIn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + [Values] bool carryIn) { - uint Opcode = 0x5A000000; // SBC W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5A000000; // SBC W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31, Carry: CarryIn); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31, carry: carryIn); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SBCS , , ")] - public void Sbcs_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sbcs_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, - [Values] bool CarryIn) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, + [Values] bool carryIn) { - uint Opcode = 0xFA000000; // SBCS X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0xFA000000; // SBCS X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31, Carry: CarryIn); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31, carry: carryIn); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SBCS , , ")] - public void Sbcs_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sbcs_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, - [Values] bool CarryIn) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, + [Values] bool carryIn) { - uint Opcode = 0x7A000000; // SBCS W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x7A000000; // SBCS W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31, Carry: CarryIn); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31, carry: carryIn); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SDIV , , ")] - public void Sdiv_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sdiv_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9AC00C00; // SDIV X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9AC00C00; // SDIV X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SDIV , , ")] - public void Sdiv_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sdiv_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) { - uint Opcode = 0x1AC00C00; // SDIV W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC00C00; // SDIV W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, #}")] - public void Sub_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xCB000000; // SUB X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xCB000000; // SUB X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, #}")] - public void Sub_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x4B000000; // SUB W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x4B000000; // SUB W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, #}")] - public void Subs_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntAmount)] uint amount) { - uint Opcode = 0xEB000000; // SUBS X0, X0, X0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0xEB000000; // SUBS X0, X0, X0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, #}")] - public void Subs_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b00u, 0b01u, 0b10u)] uint shift, // [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount) { - uint Opcode = 0x6B000000; // SUBS W0, W0, W0, LSL #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); + uint opcode = 0x6B000000; // SUBS W0, W0, W0, LSL #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((shift & 3) << 22) | ((amount & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UDIV , , ")] - public void Udiv_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Udiv_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9AC00800; // UDIV X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9AC00800; // UDIV X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UDIV , , ")] - public void Udiv_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Udiv_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm) { - uint Opcode = 0x1AC00800; // UDIV W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1AC00800; // UDIV W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRx.cs b/Ryujinx.Tests/Cpu/CpuTestAluRx.cs index 9c66532b9a..357a96ab94 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluRx.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluRx.cs @@ -1,751 +1,721 @@ #define AluRx -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("AluRx")] // Tested: second half of 2018. + [Category("AluRx")] public sealed class CpuTestAluRx : CpuTest { #if AluRx private const int RndCnt = 2; [Test, Pairwise, Description("ADD , , {, {#}}")] - public void Add_X_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_X_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF, - (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong Xm, + (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong xm, [Values(0b011u, 0b111u)] uint extend, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x8B206000; // ADD X0, X0, X0, UXTX #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x8B206000; // ADD X0, X0, X0, UXTX #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xnSp, x2: xm, x31: x31); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Xm); + SingleOpcode(opcode, x31: xnSp, x2: xm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , {, {#}}")] - public void Add_W_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_W_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint Wm, + (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: x31); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm); + SingleOpcode(opcode, x31: xnSp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , {, {#}}")] - public void Add_H_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_H_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort Wm, + (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: x31); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm); + SingleOpcode(opcode, x31: xnSp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , {, {#}}")] - public void Add_B_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_B_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm, + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: x31); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm); + SingleOpcode(opcode, x31: xnSp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , {, {#}}")] - public void Add_W_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_W_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint Wm, + (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: w31); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm); + SingleOpcode(opcode, x31: wnWsp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , {, {#}}")] - public void Add_H_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_H_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort Wm, + (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: w31); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm); + SingleOpcode(opcode, x31: wnWsp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD , , {, {#}}")] - public void Add_B_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Add_B_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm, + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: w31); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm); + SingleOpcode(opcode, x31: wnWsp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, {#}}")] - public void Adds_X_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_X_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF, - (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong Xm, + (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong xm, [Values(0b011u, 0b111u)] uint extend, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xAB206000; // ADDS X0, X0, X0, UXTX #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xAB206000; // ADDS X0, X0, X0, UXTX #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Xm, X31: Xn_SP); + SingleOpcode(opcode, x1: xnSp, x2: xm, x31: xnSp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, {#}}")] - public void Adds_W_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_W_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint Wm, + (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: xnSp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, {#}}")] - public void Adds_H_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_H_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort Wm, + (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: xnSp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, {#}}")] - public void Adds_B_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_B_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm, + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: xnSp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, {#}}")] - public void Adds_W_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_W_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint Wm, + (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: wnWsp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, {#}}")] - public void Adds_H_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_H_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort Wm, + (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: wnWsp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDS , , {, {#}}")] - public void Adds_B_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Adds_B_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm, + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: wnWsp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, {#}}")] - public void Sub_X_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_X_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF, - (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong Xm, + (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong xm, [Values(0b011u, 0b111u)] uint extend, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xCB206000; // SUB X0, X0, X0, UXTX #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xCB206000; // SUB X0, X0, X0, UXTX #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xnSp, x2: xm, x31: x31); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Xm); + SingleOpcode(opcode, x31: xnSp, x2: xm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, {#}}")] - public void Sub_W_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_W_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint Wm, + (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: x31); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm); + SingleOpcode(opcode, x31: xnSp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, {#}}")] - public void Sub_H_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_H_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort Wm, + (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: x31); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm); + SingleOpcode(opcode, x31: xnSp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, {#}}")] - public void Sub_B_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_B_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm, + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: x31); } else { - ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm); + SingleOpcode(opcode, x31: xnSp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, {#}}")] - public void Sub_W_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_W_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint Wm, + (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: w31); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm); + SingleOpcode(opcode, x31: wnWsp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, {#}}")] - public void Sub_H_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_H_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort Wm, + (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: w31); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm); + SingleOpcode(opcode, x31: wnWsp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , {, {#}}")] - public void Sub_B_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Sub_B_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm, + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState; - - if (Rn != 31) + if (rn != 31) { - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: w31); } else { - ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm); + SingleOpcode(opcode, x31: wnWsp, x2: wm); } CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, {#}}")] - public void Subs_X_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_X_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF, - (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong Xm, + (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(RndCnt)] ulong xm, [Values(0b011u, 0b111u)] uint extend, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xEB206000; // SUBS X0, X0, X0, UXTX #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xEB206000; // SUBS X0, X0, X0, UXTX #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Xm, X31: Xn_SP); + SingleOpcode(opcode, x1: xnSp, x2: xm, x31: xnSp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, {#}}")] - public void Subs_W_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_W_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint Wm, + (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: xnSp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, {#}}")] - public void Subs_H_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_H_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort Wm, + (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: xnSp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, {#}}")] - public void Subs_B_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_B_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn_SP, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xnSp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm, + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, [Values(0b000u, 0b001u, 0b010u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP); + SingleOpcode(opcode, x1: xnSp, x2: wm, x31: xnSp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, {#}}")] - public void Subs_W_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_W_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((uint)0x00000000, (uint)0x7FFFFFFF, - (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint Wm, + (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(RndCnt)] uint wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: wnWsp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, {#}}")] - public void Subs_H_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_H_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((ushort)0x0000, (ushort)0x7FFF, - (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort Wm, + (ushort)0x8000, (ushort)0xFFFF)] [Random(RndCnt)] ushort wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: wnWsp); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBS , , {, {#}}")] - public void Subs_B_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Subs_B_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn_WSP, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wnWsp, [Values((byte)0x00, (byte)0x7F, - (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte Wm, + (byte)0x80, (byte)0xFF)] [Random(RndCnt)] byte wm, [Values(0b000u, 0b001u, 0b010u, 0b011u, // [Values(0u, 1u, 2u, 3u, 4u)] uint amount) { - uint Opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); + uint opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((extend & 7) << 13) | ((amount & 7) << 10); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP); + SingleOpcode(opcode, x1: wnWsp, x2: wm, x31: wnWsp); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestBfm.cs b/Ryujinx.Tests/Cpu/CpuTestBfm.cs index 427396f8be..24f69036e4 100644 --- a/Ryujinx.Tests/Cpu/CpuTestBfm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestBfm.cs @@ -1,12 +1,10 @@ #define Bfm -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("Bfm")] // Tested: second half of 2018. + [Category("Bfm")] public sealed class CpuTestBfm : CpuTest { #if Bfm @@ -15,117 +13,117 @@ namespace Ryujinx.Tests.Cpu private const int RndCntImms = 2; [Test, Pairwise, Description("BFM , , #, #")] - public void Bfm_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Random(RndCnt)] ulong _Xd, + public void Bfm_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Random(RndCnt)] ulong xd, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr, [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImms)] uint imms) { - uint Opcode = 0xB3400000; // BFM X0, X0, #0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0xB3400000; // BFM X0, X0, #0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X0: _Xd, X1: Xn, X31: _X31); + SingleOpcode(opcode, x0: xd, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BFM , , #, #")] - public void Bfm_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Random(RndCnt)] uint _Wd, + public void Bfm_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Random(RndCnt)] uint wd, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr, [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint imms) { - uint Opcode = 0x33000000; // BFM W0, W0, #0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x33000000; // BFM W0, W0, #0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X0: _Wd, X1: Wn, X31: _W31); + SingleOpcode(opcode, x0: wd, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SBFM , , #, #")] - public void Sbfm_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Sbfm_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr, [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImms)] uint imms) { - uint Opcode = 0x93400000; // SBFM X0, X0, #0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x93400000; // SBFM X0, X0, #0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SBFM , , #, #")] - public void Sbfm_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Sbfm_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr, [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint imms) { - uint Opcode = 0x13000000; // SBFM W0, W0, #0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x13000000; // SBFM W0, W0, #0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UBFM , , #, #")] - public void Ubfm_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Ubfm_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImmr)] uint immr, [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, RndCntImms)] uint imms) { - uint Opcode = 0xD3400000; // UBFM X0, X0, #0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0xD3400000; // UBFM X0, X0, #0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UBFM , , #, #")] - public void Ubfm_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, + public void Ubfm_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint immr, [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint imms) { - uint Opcode = 0x53000000; // UBFM W0, W0, #0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); + uint opcode = 0x53000000; // UBFM W0, W0, #0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((immr & 63) << 16) | ((imms & 63) << 10); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs b/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs index 7ca92d4fea..a2c7344944 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs @@ -1,12 +1,10 @@ #define CcmpImm -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("CcmpImm")] // Tested: second half of 2018. + [Category("CcmpImm")] public sealed class CpuTestCcmpImm : CpuTest { #if CcmpImm @@ -15,9 +13,9 @@ namespace Ryujinx.Tests.Cpu private const int RndCntNzcv = 2; [Test, Pairwise, Description("CCMN , #, #, ")] - public void Ccmn_64bit([Values(1u, 31u)] uint Rn, + public void Ccmn_64bit([Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u)] [Random(0u, 31u, RndCntImm)] uint imm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0xBA400800; // CCMN X0, #0, #0, EQ - Opcode |= ((Rn & 31) << 5); - Opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0); + uint opcode = 0xBA400800; // CCMN X0, #0, #0, EQ + opcode |= ((rn & 31) << 5); + opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CCMN , #, #, ")] - public void Ccmn_32bit([Values(1u, 31u)] uint Rn, + public void Ccmn_32bit([Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 31u)] [Random(0u, 31u, RndCntImm)] uint imm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x3A400800; // CCMN W0, #0, #0, EQ - Opcode |= ((Rn & 31) << 5); - Opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0); + uint opcode = 0x3A400800; // CCMN W0, #0, #0, EQ + opcode |= ((rn & 31) << 5); + opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CCMP , #, #, ")] - public void Ccmp_64bit([Values(1u, 31u)] uint Rn, + public void Ccmp_64bit([Values(1u, 31u)] uint rn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0u, 31u)] [Random(0u, 31u, RndCntImm)] uint imm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0xFA400800; // CCMP X0, #0, #0, EQ - Opcode |= ((Rn & 31) << 5); - Opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0); + uint opcode = 0xFA400800; // CCMP X0, #0, #0, EQ + opcode |= ((rn & 31) << 5); + opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31); + SingleOpcode(opcode, x1: xn, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CCMP , #, #, ")] - public void Ccmp_32bit([Values(1u, 31u)] uint Rn, + public void Ccmp_32bit([Values(1u, 31u)] uint rn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0u, 31u)] [Random(0u, 31u, RndCntImm)] uint imm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x7A400800; // CCMP W0, #0, #0, EQ - Opcode |= ((Rn & 31) << 5); - Opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0); + uint opcode = 0x7A400800; // CCMP W0, #0, #0, EQ + opcode |= ((rn & 31) << 5); + opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31); + SingleOpcode(opcode, x1: wn, x31: w31); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs b/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs index 36120f7409..8cf5268ebf 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs @@ -1,12 +1,10 @@ #define CcmpReg -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("CcmpReg")] // Tested: second half of 2018. + [Category("CcmpReg")] public sealed class CpuTestCcmpReg : CpuTest { #if CcmpReg @@ -14,97 +12,97 @@ namespace Ryujinx.Tests.Cpu private const int RndCntNzcv = 2; [Test, Pairwise, Description("CCMN , , #, ")] - public void Ccmn_64bit([Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Ccmn_64bit([Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0xBA400000; // CCMN X0, X0, #0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5); - Opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0); + uint opcode = 0xBA400000; // CCMN X0, X0, #0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5); + opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CCMN , , #, ")] - public void Ccmn_32bit([Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Ccmn_32bit([Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x3A400000; // CCMN W0, W0, #0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5); - Opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0); + uint opcode = 0x3A400000; // CCMN W0, W0, #0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5); + opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CCMP , , #, ")] - public void Ccmp_64bit([Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Ccmp_64bit([Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0xFA400000; // CCMP X0, X0, #0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5); - Opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0); + uint opcode = 0xFA400000; // CCMP X0, X0, #0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5); + opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CCMP , , #, ")] - public void Ccmp_32bit([Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Ccmp_32bit([Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Random(0u, 15u, RndCntNzcv)] uint nzcv, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x7A400000; // CCMP W0, W0, #0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5); - Opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0); + uint opcode = 0x7A400000; // CCMP W0, W0, #0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5); + opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestCsel.cs b/Ryujinx.Tests/Cpu/CpuTestCsel.cs index 2d88b9f033..9764c2b782 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCsel.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCsel.cs @@ -1,205 +1,203 @@ #define Csel -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("Csel")] // Tested: second half of 2018. + [Category("Csel")] public sealed class CpuTestCsel : CpuTest { #if Csel private const int RndCnt = 2; [Test, Pairwise, Description("CSEL , , , ")] - public void Csel_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Csel_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x9A800000; // CSEL X0, X0, X0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((cond & 15) << 12); + uint opcode = 0x9A800000; // CSEL X0, X0, X0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((cond & 15) << 12); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CSEL , , , ")] - public void Csel_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Csel_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x1A800000; // CSEL W0, W0, W0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((cond & 15) << 12); + uint opcode = 0x1A800000; // CSEL W0, W0, W0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((cond & 15) << 12); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CSINC , , , ")] - public void Csinc_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Csinc_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x9A800400; // CSINC X0, X0, X0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((cond & 15) << 12); + uint opcode = 0x9A800400; // CSINC X0, X0, X0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((cond & 15) << 12); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CSINC , , , ")] - public void Csinc_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Csinc_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x1A800400; // CSINC W0, W0, W0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((cond & 15) << 12); + uint opcode = 0x1A800400; // CSINC W0, W0, W0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((cond & 15) << 12); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CSINV , , , ")] - public void Csinv_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Csinv_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0xDA800000; // CSINV X0, X0, X0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((cond & 15) << 12); + uint opcode = 0xDA800000; // CSINV X0, X0, X0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((cond & 15) << 12); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CSINV , , , ")] - public void Csinv_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Csinv_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x5A800000; // CSINV W0, W0, W0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((cond & 15) << 12); + uint opcode = 0x5A800000; // CSINV W0, W0, W0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((cond & 15) << 12); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CSNEG , , , ")] - public void Csneg_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Csneg_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0xDA800400; // CSNEG X0, X0, X0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((cond & 15) << 12); + uint opcode = 0xDA800400; // CSNEG X0, X0, X0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((cond & 15) << 12); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CSNEG , , , ")] - public void Csneg_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Csneg_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // { - uint Opcode = 0x5A800400; // CSNEG W0, W0, W0, EQ - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((cond & 15) << 12); + uint opcode = 0x5A800400; // CSNEG W0, W0, W0, EQ + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((cond & 15) << 12); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x31: w31); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/Ryujinx.Tests/Cpu/CpuTestMisc.cs index d7b75f5e35..4d6783f71e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMisc.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMisc.cs @@ -11,9 +11,9 @@ namespace Ryujinx.Tests.Cpu { [TestCase(0xFFFFFFFDu)] // Roots. [TestCase(0x00000005u)] - public void Misc1(uint A) + public void Misc1(uint a) { - // ((A + 3) * (A - 5)) / ((A + 5) * (A - 3)) = 0 + // ((a + 3) * (a - 5)) / ((a + 5) * (a - 3)) = 0 /* ADD W2, W0, 3 @@ -27,7 +27,7 @@ namespace Ryujinx.Tests.Cpu RET */ - SetThreadState(X0: A); + SetThreadState(x0: a); Opcode(0x11000C02); Opcode(0x51001401); Opcode(0x1B017C42); @@ -60,9 +60,9 @@ namespace Ryujinx.Tests.Cpu [TestCase( 12f, -3f)] [TestCase( 12f, 6f)] [TestCase( 20f, 5f)] - public void Misc2(float A, float B) + public void Misc2(float a, float b) { - // 1 / ((1 / A + 1 / B) ^ 2) = 16 + // 1 / ((1 / a + 1 / b) ^ 2) = 16 /* FMOV S2, 1.0e+0 @@ -76,8 +76,8 @@ namespace Ryujinx.Tests.Cpu */ SetThreadState( - V0: Sse.SetScalarVector128(A), - V1: Sse.SetScalarVector128(B)); + v0: Sse.SetScalarVector128(a), + v1: Sse.SetScalarVector128(b)); Opcode(0x1E2E1002); Opcode(0x1E201840); Opcode(0x1E211841); @@ -109,9 +109,9 @@ namespace Ryujinx.Tests.Cpu [TestCase( 12d, -3d)] [TestCase( 12d, 6d)] [TestCase( 20d, 5d)] - public void Misc3(double A, double B) + public void Misc3(double a, double b) { - // 1 / ((1 / A + 1 / B) ^ 2) = 16 + // 1 / ((1 / a + 1 / b) ^ 2) = 16 /* FMOV D2, 1.0e+0 @@ -125,8 +125,8 @@ namespace Ryujinx.Tests.Cpu */ SetThreadState( - V0: Sse.StaticCast(Sse2.SetScalarVector128(A)), - V1: Sse.StaticCast(Sse2.SetScalarVector128(B))); + v0: Sse.StaticCast(Sse2.SetScalarVector128(a)), + v1: Sse.StaticCast(Sse2.SetScalarVector128(b))); Opcode(0x1E6E1002); Opcode(0x1E601840); Opcode(0x1E611841); @@ -141,25 +141,25 @@ namespace Ryujinx.Tests.Cpu } [Test] - public void MiscF([Range(0u, 92u, 1u)] uint A) + public void MiscF([Range(0u, 92u, 1u)] uint a) { - ulong F_n(uint n) + ulong Fn(uint n) { - ulong a = 0, b = 1, c; + ulong x = 0, y = 1, z; if (n == 0) { - return a; + return x; } for (uint i = 2; i <= n; i++) { - c = a + b; - a = b; - b = c; + z = x + y; + x = y; + y = z; } - return b; + return y; } /* @@ -186,7 +186,7 @@ namespace Ryujinx.Tests.Cpu 0x0000000000001050: RET */ - SetThreadState(X0: A); + SetThreadState(x0: a); Opcode(0x2A0003E4); Opcode(0x340001C0); Opcode(0x7100041F); @@ -210,13 +210,13 @@ namespace Ryujinx.Tests.Cpu Opcode(0xD65F03C0); ExecuteOpcodes(); - Assert.That(GetThreadState().X0, Is.EqualTo(F_n(A))); + Assert.That(GetThreadState().X0, Is.EqualTo(Fn(a))); } [Test] public void MiscR() { - const ulong Result = 5; + const ulong result = 5; /* 0x0000000000001000: MOV X0, #2 @@ -233,7 +233,7 @@ namespace Ryujinx.Tests.Cpu Opcode(0xD65F03C0); ExecuteOpcodes(); - Assert.That(GetThreadState().X0, Is.EqualTo(Result)); + Assert.That(GetThreadState().X0, Is.EqualTo(result)); Reset(); @@ -252,19 +252,19 @@ namespace Ryujinx.Tests.Cpu Opcode(0xD65F03C0); ExecuteOpcodes(); - Assert.That(GetThreadState().X0, Is.EqualTo(Result)); + Assert.That(GetThreadState().X0, Is.EqualTo(result)); } [TestCase( 0ul)] [TestCase( 1ul)] [TestCase( 2ul)] [TestCase(42ul)] - public void SanityCheck(ulong A) + public void SanityCheck(ulong a) { - uint Opcode = 0xD503201F; // NOP - AThreadState ThreadState = SingleOpcode(Opcode, X0: A); + uint opcode = 0xD503201F; // NOP + CpuThreadState threadState = SingleOpcode(opcode, x0: a); - Assert.That(ThreadState.X0, Is.EqualTo(A)); + Assert.That(threadState.X0, Is.EqualTo(a)); } } } diff --git a/Ryujinx.Tests/Cpu/CpuTestMov.cs b/Ryujinx.Tests/Cpu/CpuTestMov.cs index 175c9f0687..a5ecafca0b 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMov.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMov.cs @@ -1,111 +1,109 @@ #define Mov -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("Mov")] // Tested: second half of 2018. + [Category("Mov")] public sealed class CpuTestMov : CpuTest { #if Mov private const int RndCntImm = 2; [Test, Pairwise, Description("MOVK , #{, LSL #}")] - public void Movk_64bit([Values(0u, 31u)] uint Rd, - [Random(RndCntImm)] ulong _Xd, + public void Movk_64bit([Values(0u, 31u)] uint rd, + [Random(RndCntImm)] ulong xd, [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, [Values(0u, 16u, 32u, 48u)] uint shift) { - uint Opcode = 0xF2800000; // MOVK X0, #0, LSL #0 - Opcode |= ((Rd & 31) << 0); - Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); + uint opcode = 0xF2800000; // MOVK X0, #0, LSL #0 + opcode |= ((rd & 31) << 0); + opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X0: _Xd, X31: _X31); + SingleOpcode(opcode, x0: xd, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("MOVK , #{, LSL #}")] - public void Movk_32bit([Values(0u, 31u)] uint Rd, - [Random(RndCntImm)] uint _Wd, + public void Movk_32bit([Values(0u, 31u)] uint rd, + [Random(RndCntImm)] uint wd, [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, [Values(0u, 16u)] uint shift) { - uint Opcode = 0x72800000; // MOVK W0, #0, LSL #0 - Opcode |= ((Rd & 31) << 0); - Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); + uint opcode = 0x72800000; // MOVK W0, #0, LSL #0 + opcode |= ((rd & 31) << 0); + opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X0: _Wd, X31: _W31); + SingleOpcode(opcode, x0: wd, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("MOVN , #{, LSL #}")] - public void Movn_64bit([Values(0u, 31u)] uint Rd, + public void Movn_64bit([Values(0u, 31u)] uint rd, [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, [Values(0u, 16u, 32u, 48u)] uint shift) { - uint Opcode = 0x92800000; // MOVN X0, #0, LSL #0 - Opcode |= ((Rd & 31) << 0); - Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); + uint opcode = 0x92800000; // MOVN X0, #0, LSL #0 + opcode |= ((rd & 31) << 0); + opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X31: _X31); + SingleOpcode(opcode, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("MOVN , #{, LSL #}")] - public void Movn_32bit([Values(0u, 31u)] uint Rd, + public void Movn_32bit([Values(0u, 31u)] uint rd, [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, [Values(0u, 16u)] uint shift) { - uint Opcode = 0x12800000; // MOVN W0, #0, LSL #0 - Opcode |= ((Rd & 31) << 0); - Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); + uint opcode = 0x12800000; // MOVN W0, #0, LSL #0 + opcode |= ((rd & 31) << 0); + opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X31: _W31); + SingleOpcode(opcode, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("MOVZ , #{, LSL #}")] - public void Movz_64bit([Values(0u, 31u)] uint Rd, + public void Movz_64bit([Values(0u, 31u)] uint rd, [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, [Values(0u, 16u, 32u, 48u)] uint shift) { - uint Opcode = 0xD2800000; // MOVZ X0, #0, LSL #0 - Opcode |= ((Rd & 31) << 0); - Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); + uint opcode = 0xD2800000; // MOVZ X0, #0, LSL #0 + opcode |= ((rd & 31) << 0); + opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X31: _X31); + SingleOpcode(opcode, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("MOVZ , #{, LSL #}")] - public void Movz_32bit([Values(0u, 31u)] uint Rd, + public void Movz_32bit([Values(0u, 31u)] uint rd, [Values(0u, 65535u)] [Random(0u, 65535u, RndCntImm)] uint imm, [Values(0u, 16u)] uint shift) { - uint Opcode = 0x52800000; // MOVZ W0, #0, LSL #0 - Opcode |= ((Rd & 31) << 0); - Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); + uint opcode = 0x52800000; // MOVZ W0, #0, LSL #0 + opcode |= ((rd & 31) << 0); + opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X31: _W31); + SingleOpcode(opcode, x31: w31); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestMul.cs b/Ryujinx.Tests/Cpu/CpuTestMul.cs index 056c45437a..4ad7cf1104 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMul.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMul.cs @@ -1,227 +1,225 @@ #define Mul -using ChocolArm64.State; - using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [Category("Mul")] // Tested: second half of 2018. + [Category("Mul")] public sealed class CpuTestMul : CpuTest { #if Mul private const int RndCnt = 2; [Test, Pairwise, Description("MADD , , , ")] - public void Madd_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(3u, 31u)] uint Ra, + public void Madd_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(3u, 31u)] uint ra, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) { - uint Opcode = 0x9B000000; // MADD X0, X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9B000000; // MADD X0, X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X3: Xa, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x3: xa, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("MADD , , , ")] - public void Madd_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(3u, 31u)] uint Ra, + public void Madd_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wa) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wa) { - uint Opcode = 0x1B000000; // MADD W0, W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1B000000; // MADD W0, W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Wa, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x3: wa, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("MSUB , , , ")] - public void Msub_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(3u, 31u)] uint Ra, + public void Msub_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(3u, 31u)] uint ra, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) { - uint Opcode = 0x9B008000; // MSUB X0, X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9B008000; // MSUB X0, X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X3: Xa, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x3: xa, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("MSUB , , , ")] - public void Msub_32bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(3u, 31u)] uint Ra, + public void Msub_32bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wa) + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wa) { - uint Opcode = 0x1B008000; // MSUB W0, W0, W0, W0 - Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x1B008000; // MSUB W0, W0, W0, W0 + opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); - uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + uint w31 = TestContext.CurrentContext.Random.NextUInt(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Wa, X31: _W31); + SingleOpcode(opcode, x1: wn, x2: wm, x3: wa, x31: w31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SMADDL , , , ")] - public void Smaddl_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(3u, 31u)] uint Ra, + public void Smaddl_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) { - uint Opcode = 0x9B200000; // SMADDL X0, W0, W0, X0 - Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9B200000; // SMADDL X0, W0, W0, X0 + opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Xa, X31: _X31); + SingleOpcode(opcode, x1: wn, x2: wm, x3: xa, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UMADDL , , , ")] - public void Umaddl_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(3u, 31u)] uint Ra, + public void Umaddl_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) { - uint Opcode = 0x9BA00000; // UMADDL X0, W0, W0, X0 - Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9BA00000; // UMADDL X0, W0, W0, X0 + opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Xa, X31: _X31); + SingleOpcode(opcode, x1: wn, x2: wm, x3: xa, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SMSUBL , , , ")] - public void Smsubl_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(3u, 31u)] uint Ra, + public void Smsubl_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) { - uint Opcode = 0x9B208000; // SMSUBL X0, W0, W0, X0 - Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9B208000; // SMSUBL X0, W0, W0, X0 + opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Xa, X31: _X31); + SingleOpcode(opcode, x1: wn, x2: wm, x3: xa, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UMSUBL , , , ")] - public void Umsubl_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, - [Values(3u, 31u)] uint Ra, + public void Umsubl_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, + [Values(3u, 31u)] uint ra, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wn, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, [Values(0x00000000u, 0x7FFFFFFFu, - 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint Wm, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xa) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xa) { - uint Opcode = 0x9BA08000; // UMSUBL X0, W0, W0, X0 - Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9BA08000; // UMSUBL X0, W0, W0, X0 + opcode |= ((rm & 31) << 16) | ((ra & 31) << 10) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Xa, X31: _X31); + SingleOpcode(opcode, x1: wn, x2: wm, x3: xa, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SMULH , , ")] - public void Smulh_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Smulh_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9B407C00; // SMULH X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9B407C00; // SMULH X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UMULH , , ")] - public void Umulh_64bit([Values(0u, 31u)] uint Rd, - [Values(1u, 31u)] uint Rn, - [Values(2u, 31u)] uint Rm, + public void Umulh_64bit([Values(0u, 31u)] uint rd, + [Values(1u, 31u)] uint rn, + [Values(2u, 31u)] uint rm, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xn, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, - 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong Xm) + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xm) { - uint Opcode = 0x9BC07C00; // UMULH X0, X0, X0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x9BC07C00; // UMULH X0, X0, X0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + ulong x31 = TestContext.CurrentContext.Random.NextULong(); - AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31); + SingleOpcode(opcode, x1: xn, x2: xm, x31: x31); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index ec0cd104fd..54889eee34 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -1,19 +1,18 @@ #define Simd -using ChocolArm64.State; - using NUnit.Framework; +using System.Collections.Generic; using System.Runtime.Intrinsics; namespace Ryujinx.Tests.Cpu { - [Category("Simd")] // Tested: second half of 2018. + [Category("Simd")] public sealed class CpuTestSimd : CpuTest { #if Simd -#region "ValueSource" +#region "ValueSource (Types)" private static ulong[] _1B1H1S1D_() { return new ulong[] { 0x0000000000000000ul, 0x000000000000007Ful, @@ -40,6 +39,18 @@ namespace Ryujinx.Tests.Cpu 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } + private static ulong[] _1S_() + { + return new ulong[] { 0x0000000000000000ul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul }; + } + + private static ulong[] _2S_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + private static ulong[] _4H2S1D_() { return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, @@ -78,1580 +89,2088 @@ namespace Ryujinx.Tests.Cpu 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } - private static ulong[] _1S_F_() + private static IEnumerable _4H_F_() { - return new ulong[] + yield return 0xFBFFFBFFFBFFFBFFul; // -Max Normal + yield return 0x8400840084008400ul; // -Min Normal + yield return 0x83FF83FF83FF83FFul; // -Max Subnormal + yield return 0x8001800180018001ul; // -Min Subnormal + yield return 0x7BFF7BFF7BFF7BFFul; // +Max Normal + yield return 0x0400040004000400ul; // +Min Normal + yield return 0x03FF03FF03FF03FFul; // +Max Subnormal + yield return 0x0001000100010001ul; // +Min Subnormal + + if (!NoZeros) { - 0x00000000FFFFFFFFul, // -QNaN (all ones payload) - 0x00000000FFBFFFFFul, // -SNaN (all ones payload) - 0x00000000FF800000ul, // -INF - 0x00000000FF7FFFFFul, // -Max Normal, float.MinValue - 0x0000000080800000ul, // -Min Normal - 0x00000000807FFFFFul, // -Max SubNormal - 0x0000000080000001ul, // -Min SubNormal - 0x0000000080000000ul, // -0 - 0x0000000000000000ul, // +0 - 0x0000000000000001ul, // +Min SubNormal - 0x00000000007FFFFFul, // +Max SubNormal - 0x0000000000800000ul, // +Min Normal - 0x000000007F7FFFFFul, // +Max Normal, float.MaxValue - 0x000000007F800000ul, // +INF - 0x000000007FBFFFFFul, // +SNaN (all ones payload) - 0x000000007FFFFFFFul // +QNaN (all ones payload) + yield return 0x8000800080008000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFC00FC00FC00FC00ul; // -Infinity + yield return 0x7C007C007C007C00ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFE00FE00FE00FE00ul; // -QNaN (all zeros payload) + yield return 0xFDFFFDFFFDFFFDFFul; // -SNaN (all ones payload) + yield return 0x7E007E007E007E00ul; // +QNaN (all zeros payload) (DefaultNaN) + yield return 0x7DFF7DFF7DFF7DFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + uint rnd1 = (uint)GenNormalH(); + uint rnd2 = (uint)GenSubnormalH(); + + yield return (rnd1 << 48) | (rnd1 << 32) | (rnd1 << 16) | rnd1; + yield return (rnd2 << 48) | (rnd2 << 32) | (rnd2 << 16) | rnd2; + } + } + + private static IEnumerable _1S_F_() + { + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x0000000080800000ul; // -Min Normal + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0000000000800000ul; // +Min Normal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x0000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0x00000000FF800000ul; // -Infinity + yield return 0x000000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong grbg = TestContext.CurrentContext.Random.NextUInt(); + ulong rnd1 = GenNormalS(); + ulong rnd2 = GenSubnormalS(); + + yield return (grbg << 32) | rnd1; + yield return (grbg << 32) | rnd2; + } + } + + private static IEnumerable _2S_F_() + { + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x8080000080800000ul; // -Min Normal + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0080000000800000ul; // +Min Normal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFF800000FF800000ul; // -Infinity + yield return 0x7F8000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = GenNormalS(); + ulong rnd2 = GenSubnormalS(); + + yield return (rnd1 << 32) | rnd1; + yield return (rnd2 << 32) | rnd2; + } + } + + private static IEnumerable _1D_F_() + { + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = GenNormalD(); + ulong rnd2 = GenSubnormalD(); + + yield return rnd1; + yield return rnd2; + } + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _F_Cmp_Cmpe_S_S_() + { + return new uint[] + { + 0x1E202028u, // FCMP S1, #0.0 + 0x1E202038u // FCMPE S1, #0.0 }; } - private static ulong[] _2S_F_() + private static uint[] _F_Cmp_Cmpe_S_D_() { - return new ulong[] + return new uint[] { - 0xFFFFFFFFFFFFFFFFul, // -QNaN (all ones payload) - 0xFFBFFFFFFFBFFFFFul, // -SNaN (all ones payload) - 0xFF800000FF800000ul, // -INF - 0xFF7FFFFFFF7FFFFFul, // -Max Normal, float.MinValue - 0x8080000080800000ul, // -Min Normal - 0x807FFFFF807FFFFFul, // -Max SubNormal - 0x8000000180000001ul, // -Min SubNormal - 0x8000000080000000ul, // -0 - 0x0000000000000000ul, // +0 - 0x0000000100000001ul, // +Min SubNormal - 0x007FFFFF007FFFFFul, // +Max SubNormal - 0x0080000000800000ul, // +Min Normal - 0x7F7FFFFF7F7FFFFFul, // +Max Normal, float.MaxValue - 0x7F8000007F800000ul, // +INF - 0x7FBFFFFF7FBFFFFFul, // +SNaN (all ones payload) - 0x7FFFFFFF7FFFFFFFul // +QNaN (all ones payload) + 0x1E602028u, // FCMP D1, #0.0 + 0x1E602038u // FCMPE D1, #0.0 }; } - private static ulong[] _1D_F_() + private static uint[] _F_Cvt_S_SD_() { - return new ulong[] + return new uint[] { - 0xFFFFFFFFFFFFFFFFul, // -QNaN (all ones payload) - 0xFFF7FFFFFFFFFFFFul, // -SNaN (all ones payload) - 0xFFF0000000000000ul, // -INF - 0xFFEFFFFFFFFFFFFFul, // -Max Normal, double.MinValue - 0x8010000000000000ul, // -Min Normal - 0x800FFFFFFFFFFFFFul, // -Max SubNormal - 0x8000000000000001ul, // -Min SubNormal - 0x8000000000000000ul, // -0 - 0x0000000000000000ul, // +0 - 0x0000000000000001ul, // +Min SubNormal - 0x000FFFFFFFFFFFFFul, // +Max SubNormal - 0x0010000000000000ul, // +Min Normal - 0x7FEFFFFFFFFFFFFFul, // +Max Normal, double.MaxValue - 0x7FF0000000000000ul, // +INF - 0x7FF7FFFFFFFFFFFFul, // +SNaN (all ones payload) - 0x7FFFFFFFFFFFFFFFul // +QNaN (all ones payload) + 0x1E22C020u // FCVT D0, S1 + }; + } + + private static uint[] _F_Cvt_S_DS_() + { + return new uint[] + { + 0x1E624020u // FCVT S0, D1 + }; + } + + private static uint[] _F_Cvt_NZ_SU_S_S_() + { + return new uint[] + { + 0x5E21A820u, // FCVTNS S0, S1 + 0x7E21A820u, // FCVTNU S0, S1 + 0x5EA1B820u, // FCVTZS S0, S1 + 0x7EA1B820u // FCVTZU S0, S1 + }; + } + + private static uint[] _F_Cvt_NZ_SU_S_D_() + { + return new uint[] + { + 0x5E61A820u, // FCVTNS D0, D1 + 0x7E61A820u, // FCVTNU D0, D1 + 0x5EE1B820u, // FCVTZS D0, D1 + 0x7EE1B820u // FCVTZU D0, D1 + }; + } + + private static uint[] _F_Cvt_NZ_SU_V_2S_4S_() + { + return new uint[] + { + 0x0E21A800u, // FCVTNS V0.2S, V0.2S + 0x2E21A800u, // FCVTNU V0.2S, V0.2S + 0x0EA1B800u, // FCVTZS V0.2S, V0.2S + 0x2EA1B800u // FCVTZU V0.2S, V0.2S + }; + } + + private static uint[] _F_Cvt_NZ_SU_V_2D_() + { + return new uint[] + { + 0x4E61A800u, // FCVTNS V0.2D, V0.2D + 0x6E61A800u, // FCVTNU V0.2D, V0.2D + 0x4EE1B800u, // FCVTZS V0.2D, V0.2D + 0x6EE1B800u // FCVTZU V0.2D, V0.2D + }; + } + + private static uint[] _F_Cvtl_V_4H4S_8H4S_() + { + return new uint[] + { + 0x0E217800u // FCVTL V0.4S, V0.4H + }; + } + + private static uint[] _F_Cvtl_V_2S2D_4S2D_() + { + return new uint[] + { + 0x0E617800u // FCVTL V0.2D, V0.2S + }; + } + + private static uint[] _F_Cvtn_V_4S4H_4S8H_() + { + return new uint[] + { + 0x0E216800u // FCVTN V0.4H, V0.4S + }; + } + + private static uint[] _F_Cvtn_V_2D2S_2D4S_() + { + return new uint[] + { + 0x0E616800u // FCVTN V0.2S, V0.2D + }; + } + + private static uint[] _F_Abs_Neg_Recpx_Sqrt_S_S_() + { + return new uint[] + { + 0x1E20C020u, // FABS S0, S1 + 0x1E214020u, // FNEG S0, S1 + 0x5EA1F820u, // FRECPX S0, S1 + 0x1E21C020u // FSQRT S0, S1 + }; + } + + private static uint[] _F_Abs_Neg_Recpx_Sqrt_S_D_() + { + return new uint[] + { + 0x1E60C020u, // FABS D0, D1 + 0x1E614020u, // FNEG D0, D1 + 0x5EE1F820u, // FRECPX D0, D1 + 0x1E61C020u // FSQRT D0, D1 + }; + } + + private static uint[] _F_Abs_Neg_Sqrt_V_2S_4S_() + { + return new uint[] + { + 0x0EA0F800u, // FABS V0.2S, V0.2S + 0x2EA0F800u, // FNEG V0.2S, V0.2S + 0x2EA1F800u // FSQRT V0.2S, V0.2S + }; + } + + private static uint[] _F_Abs_Neg_Sqrt_V_2D_() + { + return new uint[] + { + 0x4EE0F800u, // FABS V0.2D, V0.2D + 0x6EE0F800u, // FNEG V0.2D, V0.2D + 0x6EE1F800u // FSQRT V0.2D, V0.2D + }; + } + + private static uint[] _SU_Cvt_F_S_S_() + { + return new uint[] + { + 0x5E21D820u, // SCVTF S0, S1 + 0x7E21D820u // UCVTF S0, S1 + }; + } + + private static uint[] _SU_Cvt_F_S_D_() + { + return new uint[] + { + 0x5E61D820u, // SCVTF D0, D1 + 0x7E61D820u // UCVTF D0, D1 + }; + } + + private static uint[] _SU_Cvt_F_V_2S_4S_() + { + return new uint[] + { + 0x0E21D800u, // SCVTF V0.2S, V0.2S + 0x2E21D800u // UCVTF V0.2S, V0.2S + }; + } + + private static uint[] _SU_Cvt_F_V_2D_() + { + return new uint[] + { + 0x4E61D800u, // SCVTF V0.2D, V0.2D + 0x6E61D800u // UCVTF V0.2D, V0.2D + }; + } + + private static uint[] _Sha1h_Sha1su1_V_() + { + return new uint[] + { + 0x5E280800u, // SHA1H S0, S0 + 0x5E281800u // SHA1SU1 V0.4S, V0.4S + }; + } + + private static uint[] _Sha256su0_V_() + { + return new uint[] + { + 0x5E282800u // SHA256SU0 V0.4S, V0.4S }; } #endregion private const int RndCnt = 2; + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + [Test, Pairwise, Description("ABS , ")] - public void Abs_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A) + public void Abs_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x5EE0B800; // ABS D0, D0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EE0B800; // ABS D0, D0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ABS ., .")] - public void Abs_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Abs_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E20B800; // ABS V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E20B800; // ABS V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ABS ., .")] - public void Abs_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Abs_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E20B800; // ABS V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E20B800; // ABS V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDP , .")] - public void Addp_S_2DD([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A) + public void Addp_S_2DD([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x5EF1B800; // ADDP D0, V0.2D - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EF1B800; // ADDP D0, V0.2D + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDV , .")] - public void Addv_V_8BB_4HH([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong A, + public void Addv_V_8BB_4HH([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u)] uint size) // <8BB, 4HH> { - uint Opcode = 0x0E31B800; // ADDV B0, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E31B800; // ADDV B0, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDV , .")] - public void Addv_V_16BB_8HH_4SS([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Addv_V_16BB_8HH_4SS([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16BB, 8HH, 4SS> { - uint Opcode = 0x4E31B800; // ADDV B0, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E31B800; // ADDV B0, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CLS ., .")] - public void Cls_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Cls_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E204800; // CLS V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E204800; // CLS V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CLS ., .")] - public void Cls_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Cls_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E204800; // CLS V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E204800; // CLS V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CLZ ., .")] - public void Clz_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Clz_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E204800; // CLZ V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E204800; // CLZ V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CLZ ., .")] - public void Clz_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Clz_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x6E204800; // CLZ V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E204800; // CLZ V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMEQ , , #0")] - public void Cmeq_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A) + public void Cmeq_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x5EE09800; // CMEQ D0, D0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EE09800; // CMEQ D0, D0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMEQ ., ., #0")] - public void Cmeq_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Cmeq_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E209800; // CMEQ V0.8B, V0.8B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E209800; // CMEQ V0.8B, V0.8B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMEQ ., ., #0")] - public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E209800; // CMEQ V0.16B, V0.16B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E209800; // CMEQ V0.16B, V0.16B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGE , , #0")] - public void Cmge_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A) + public void Cmge_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x7EE08800; // CMGE D0, D0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x7EE08800; // CMGE D0, D0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGE ., ., #0")] - public void Cmge_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Cmge_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E208800; // CMGE V0.8B, V0.8B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E208800; // CMGE V0.8B, V0.8B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGE ., ., #0")] - public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E208800; // CMGE V0.16B, V0.16B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E208800; // CMGE V0.16B, V0.16B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGT , , #0")] - public void Cmgt_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A) + public void Cmgt_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x5EE08800; // CMGT D0, D0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EE08800; // CMGT D0, D0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGT ., ., #0")] - public void Cmgt_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Cmgt_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E208800; // CMGT V0.8B, V0.8B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E208800; // CMGT V0.8B, V0.8B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGT ., ., #0")] - public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E208800; // CMGT V0.16B, V0.16B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E208800; // CMGT V0.16B, V0.16B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMLE , , #0")] - public void Cmle_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A) + public void Cmle_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x7EE09800; // CMLE D0, D0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x7EE09800; // CMLE D0, D0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMLE ., ., #0")] - public void Cmle_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Cmle_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E209800; // CMLE V0.8B, V0.8B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E209800; // CMLE V0.8B, V0.8B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMLE ., ., #0")] - public void Cmle_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Cmle_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E209800; // CMLE V0.16B, V0.16B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E209800; // CMLE V0.16B, V0.16B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMLT , , #0")] - public void Cmlt_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A) + public void Cmlt_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x5EE0A800; // CMLT D0, D0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EE0A800; // CMLT D0, D0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMLT ., ., #0")] - public void Cmlt_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Cmlt_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E20A800; // CMLT V0.8B, V0.8B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E20A800; // CMLT V0.8B, V0.8B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMLT ., ., #0")] - public void Cmlt_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Cmlt_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E20A800; // CMLT V0.16B, V0.16B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E20A800; // CMLT V0.16B, V0.16B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CNT ., .")] - public void Cnt_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A) + public void Cnt_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x0E205800; // CNT V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x0E205800; // CNT V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CNT ., .")] - public void Cnt_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A) + public void Cnt_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x4E205800; // CNT V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4E205800; // CNT V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FCVTNS , ")] - public void Fcvtns_S_S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1S_F_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1S_F_")] [Random(RndCnt)] ulong A) + [Test, Pairwise] [Explicit] + public void F_Cmp_Cmpe_S_S([ValueSource("_F_Cmp_Cmpe_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a) { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + Vector128 v1 = MakeVectorE0(a); - uint Opcode = 0x5E21A800; // FCVTNS S0, S0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + bool v = TestContext.CurrentContext.Random.NextBool(); + bool c = TestContext.CurrentContext.Random.NextBool(); + bool z = TestContext.CurrentContext.Random.NextBool(); + bool n = TestContext.CurrentContext.Random.NextBool(); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. + SingleOpcode(opcodes, v1: v1, overflow: v, carry: c, zero: z, negative: n); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); } - [Test, Pairwise, Description("FCVTNS , ")] - public void Fcvtns_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_F_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_F_")] [Random(RndCnt)] ulong A) + [Test, Pairwise] [Explicit] + public void F_Cmp_Cmpe_S_D([ValueSource("_F_Cmp_Cmpe_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a) { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + Vector128 v1 = MakeVectorE0(a); - uint Opcode = 0x5E61A800; // FCVTNS D0, D0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + bool v = TestContext.CurrentContext.Random.NextBool(); + bool c = TestContext.CurrentContext.Random.NextBool(); + bool z = TestContext.CurrentContext.Random.NextBool(); + bool n = TestContext.CurrentContext.Random.NextBool(); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. + SingleOpcode(opcodes, v1: v1, overflow: v, carry: c, zero: z, negative: n); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); } - [Test, Pairwise, Description("FCVTNS ., .")] - public void Fcvtns_V_2S_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_2S_F_")] [Random(RndCnt)] ulong Z, - [ValueSource("_2S_F_")] [Random(RndCnt)] ulong A, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + [Test, Pairwise] [Explicit] + public void F_Cvt_S_SD([ValueSource("_F_Cvt_S_SD_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a) { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0(a); - uint Opcode = 0x0E21A800; // FCVTNS V0.2S, V0.2S - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((Q & 1) << 30); + SingleOpcode(opcodes, v0: v0, v1: v1); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FCVTNS ., .")] - public void Fcvtns_V_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_F_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_F_")] [Random(RndCnt)] ulong A) + [Test, Pairwise] [Explicit] + public void F_Cvt_S_DS([ValueSource("_F_Cvt_S_DS_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a) { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - uint Opcode = 0x4E61A800; // FCVTNS V0.2D, V0.2D - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + SingleOpcode(opcodes, v0: v0, v1: v1); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FCVTNU , ")] - public void Fcvtnu_S_S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1S_F_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1S_F_")] [Random(RndCnt)] ulong A) + [Test, Pairwise] [Explicit] + public void F_Cvt_NZ_SU_S_S([ValueSource("_F_Cvt_NZ_SU_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a) { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - uint Opcode = 0x7E21A800; // FCVTNU S0, S0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + SingleOpcode(opcodes, v0: v0, v1: v1); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FCVTNU , ")] - public void Fcvtnu_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_F_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_F_")] [Random(RndCnt)] ulong A) + [Test, Pairwise] [Explicit] + public void F_Cvt_NZ_SU_S_D([ValueSource("_F_Cvt_NZ_SU_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a) { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0(a); - uint Opcode = 0x7E61A800; // FCVTNU D0, D0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + SingleOpcode(opcodes, v0: v0, v1: v1); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FCVTNU ., .")] - public void Fcvtnu_V_2S_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_2S_F_")] [Random(RndCnt)] ulong Z, - [ValueSource("_2S_F_")] [Random(RndCnt)] ulong A, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + [Test, Pairwise] [Explicit] + public void F_Cvt_NZ_SU_V_2S_4S([ValueSource("_F_Cvt_NZ_SU_V_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); - uint Opcode = 0x2E21A800; // FCVTNU V0.2S, V0.2S - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((Q & 1) << 30); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. + SingleOpcode(opcodes, v0: v0, v1: v1); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FCVTNU ., .")] - public void Fcvtnu_V_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_F_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_F_")] [Random(RndCnt)] ulong A) + [Test, Pairwise] [Explicit] + public void F_Cvt_NZ_SU_V_2D([ValueSource("_F_Cvt_NZ_SU_V_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a) { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); - uint Opcode = 0x6E61A800; // FCVTNU V0.2D, V0.2D - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. + SingleOpcode(opcodes, v0: v0, v1: v1); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + CompareAgainstUnicorn(); + } - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); + [Test, Pairwise] [Explicit] + public void F_Cvtl_V_4H4S_8H4S([ValueSource("_F_Cvtl_V_4H4S_8H4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H_F_")] ulong z, + [ValueSource("_4H_F_")] ulong a, + [Values(0b0u, 0b1u)] uint q, // <4H, 8H> + [Values(RMode.Rn)] RMode rMode) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + Vector128 v0 = MakeVectorE0E1(q == 0u ? z : 0ul, q == 1u ? z : 0ul); + Vector128 v1 = MakeVectorE0E1(q == 0u ? a : 0ul, q == 1u ? a : 0ul); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = (int)rMode << (int)Fpcr.RMode; + fpcr |= rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + fpcr |= rnd & (1 << (int)Fpcr.Ahp); + + SingleOpcode(opcodes, v0: v0, v1: v1, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Ofc | Fpsr.Ufc | Fpsr.Ixc); + } + + [Test, Pairwise] [Explicit] + public void F_Cvtl_V_2S2D_4S2D([ValueSource("_F_Cvtl_V_2S2D_4S2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(q == 0u ? z : 0ul, q == 1u ? z : 0ul); + Vector128 v1 = MakeVectorE0E1(q == 0u ? a : 0ul, q == 1u ? a : 0ul); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] // Unicorn seems to default all rounding modes to RMode.Rn. + public void F_Cvtn_V_4S4H_4S8H([ValueSource("_F_Cvtn_V_4S4H_4S8H_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [Values(0b0u, 0b1u)] uint q, // <4H, 8H> + [Values(RMode.Rn)] RMode rMode) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = (int)rMode << (int)Fpcr.RMode; + fpcr |= rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + fpcr |= rnd & (1 << (int)Fpcr.Ahp); + + SingleOpcode(opcodes, v0: v0, v1: v1, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Ofc | Fpsr.Ufc | Fpsr.Ixc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] // Unicorn seems to default all rounding modes to RMode.Rn. + public void F_Cvtn_V_2D2S_2D4S([ValueSource("_F_Cvtn_V_2D2S_2D4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Abs_Neg_Recpx_Sqrt_S_S([ValueSource("_F_Abs_Neg_Recpx_Sqrt_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, z); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Abs_Neg_Recpx_Sqrt_S_D([ValueSource("_F_Abs_Neg_Recpx_Sqrt_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0E1(a, z); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Abs_Neg_Sqrt_V_2S_4S([ValueSource("_F_Abs_Neg_Sqrt_V_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Abs_Neg_Sqrt_V_2D([ValueSource("_F_Abs_Neg_Sqrt_V_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); } [Test, Pairwise, Description("NEG , ")] - public void Neg_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A) + public void Neg_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x7EE0B800; // NEG D0, D0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x7EE0B800; // NEG D0, D0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("NEG ., .")] - public void Neg_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Neg_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E20B800; // NEG V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E20B800; // NEG V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("NEG ., .")] - public void Neg_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Neg_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E20B800; // NEG V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E20B800; // NEG V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("NOT ., .")] - public void Not_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A) + public void Not_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x2E205800; // NOT V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x2E205800; // NOT V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("NOT ., .")] - public void Not_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A) + public void Not_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x6E205800; // NOT V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x6E205800; // NOT V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RBIT ., .")] - public void Rbit_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A) + public void Rbit_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x2E605800; // RBIT V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x2E605800; // RBIT V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RBIT ., .")] - public void Rbit_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A) + public void Rbit_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x6E605800; // RBIT V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x6E605800; // RBIT V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV16 ., .")] - public void Rev16_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A) + public void Rev16_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x0E201800; // REV16 V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x0E201800; // REV16 V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV16 ., .")] - public void Rev16_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A) + public void Rev16_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x4E201800; // REV16 V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4E201800; // REV16 V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV32 ., .")] - public void Rev32_V_8B_4H([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong A, + public void Rev32_V_8B_4H([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u)] uint size) // <8B, 4H> { - uint Opcode = 0x2E200800; // REV32 V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E200800; // REV32 V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV32 ., .")] - public void Rev32_V_16B_8H([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H_")] [Random(RndCnt)] ulong A, + public void Rev32_V_16B_8H([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u)] uint size) // <16B, 8H> { - uint Opcode = 0x6E200800; // REV32 V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E200800; // REV32 V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV64 ., .")] - public void Rev64_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Rev64_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E200800; // REV64 V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E200800; // REV64 V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("REV64 ., .")] - public void Rev64_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Rev64_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E200800; // REV64 V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E200800; // REV64 V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SADALP ., .")] - public void Sadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Sadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> { - uint Opcode = 0x0E206800; // SADALP V0.4H, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E206800; // SADALP V0.4H, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SADALP ., .")] - public void Sadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Sadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E206800; // SADALP V0.8H, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E206800; // SADALP V0.8H, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SADDLP ., .")] - public void Saddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Saddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> { - uint Opcode = 0x0E202800; // SADDLP V0.4H, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E202800; // SADDLP V0.4H, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SADDLP ., .")] - public void Saddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Saddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E202800; // SADDLP V0.8H, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E202800; // SADDLP V0.8H, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } - [Test, Pairwise, Description("SHA256SU0 .4S, .4S")] - public void Sha256su0_V([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Random(RndCnt / 2)] ulong Z0, [Random(RndCnt / 2)] ulong Z1, - [Random(RndCnt / 2)] ulong A0, [Random(RndCnt / 2)] ulong A1) + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_S_S([ValueSource("_SU_Cvt_F_S_S_")] uint opcodes, + [ValueSource("_1S_")] [Random(RndCnt)] ulong a) { - uint Opcode = 0x5E282800; // SHA256SU0 V0.4S, V0.4S - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - Vector128 V0 = MakeVectorE0E1(Z0, Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); + SingleOpcode(opcodes, v0: v0, v1: v1); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_S_D([ValueSource("_SU_Cvt_F_S_D_")] uint opcodes, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_V_2S_4S([ValueSource("_SU_Cvt_F_V_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_V_2D([ValueSource("_SU_Cvt_F_V_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Sha1h_Sha1su1_V([ValueSource("_Sha1h_Sha1su1_V_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, + [Random(RndCnt / 2)] ulong a0, [Random(RndCnt / 2)] ulong a1) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z0, z1); + Vector128 v1 = MakeVectorE0E1(a0, a1); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Sha256su0_V([ValueSource("_Sha256su0_V_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, + [Random(RndCnt / 2)] ulong a0, [Random(RndCnt / 2)] ulong a1) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z0, z1); + Vector128 v1 = MakeVectorE0E1(a0, a1); + + SingleOpcode(opcodes, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SQABS , ")] - public void Sqabs_S_B_H_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + public void Sqabs_S_B_H_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { - uint Opcode = 0x5E207800; // SQABS B0, B0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x5E207800; // SQABS B0, B0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQABS ., .")] - public void Sqabs_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Sqabs_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E207800; // SQABS V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E207800; // SQABS V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQABS ., .")] - public void Sqabs_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Sqabs_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E207800; // SQABS V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E207800; // SQABS V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQNEG , ")] - public void Sqneg_S_B_H_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + public void Sqneg_S_B_H_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { - uint Opcode = 0x7E207800; // SQNEG B0, B0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x7E207800; // SQNEG B0, B0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQNEG ., .")] - public void Sqneg_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Sqneg_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E207800; // SQNEG V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E207800; // SQNEG V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQNEG ., .")] - public void Sqneg_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Sqneg_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E207800; // SQNEG V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E207800; // SQNEG V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQXTN , ")] - public void Sqxtn_S_HB_SH_DS([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, + public void Sqxtn_S_HB_SH_DS([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // { - uint Opcode = 0x5E214800; // SQXTN B0, H0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x5E214800; // SQXTN B0, H0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQXTN{2} ., .")] - public void Sqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + public void Sqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x0E214800; // SQXTN V0.8B, V0.8H - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E214800; // SQXTN V0.8B, V0.8H + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQXTN{2} ., .")] - public void Sqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + public void Sqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x4E214800; // SQXTN2 V0.16B, V0.8H - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E214800; // SQXTN2 V0.16B, V0.8H + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQXTUN , ")] - public void Sqxtun_S_HB_SH_DS([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, + public void Sqxtun_S_HB_SH_DS([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // { - uint Opcode = 0x7E212800; // SQXTUN B0, H0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x7E212800; // SQXTUN B0, H0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQXTUN{2} ., .")] - public void Sqxtun_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + public void Sqxtun_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x2E212800; // SQXTUN V0.8B, V0.8H - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E212800; // SQXTUN V0.8B, V0.8H + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQXTUN{2} ., .")] - public void Sqxtun_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + public void Sqxtun_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x6E212800; // SQXTUN2 V0.16B, V0.8H - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E212800; // SQXTUN2 V0.16B, V0.8H + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SUQADD , ")] - public void Suqadd_S_B_H_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + public void Suqadd_S_B_H_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { - uint Opcode = 0x5E203800; // SUQADD B0, B0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x5E203800; // SUQADD B0, B0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SUQADD ., .")] - public void Suqadd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Suqadd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E203800; // SUQADD V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E203800; // SUQADD V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SUQADD ., .")] - public void Suqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Suqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E203800; // SUQADD V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E203800; // SUQADD V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("UADALP ., .")] - public void Uadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Uadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> { - uint Opcode = 0x2E206800; // UADALP V0.4H, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E206800; // UADALP V0.4H, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UADALP ., .")] - public void Uadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Uadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E206800; // UADALP V0.8H, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E206800; // UADALP V0.8H, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UADDLP ., .")] - public void Uaddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Uaddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> { - uint Opcode = 0x2E202800; // UADDLP V0.4H, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E202800; // UADDLP V0.4H, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UADDLP ., .")] - public void Uaddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Uaddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E202800; // UADDLP V0.8H, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E202800; // UADDLP V0.8H, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UQXTN , ")] - public void Uqxtn_S_HB_SH_DS([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, + public void Uqxtn_S_HB_SH_DS([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // { - uint Opcode = 0x7E214800; // UQXTN B0, H0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x7E214800; // UQXTN B0, H0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("UQXTN{2} ., .")] - public void Uqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + public void Uqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x2E214800; // UQXTN V0.8B, V0.8H - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E214800; // UQXTN V0.8B, V0.8H + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("UQXTN{2} ., .")] - public void Uqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + public void Uqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x6E214800; // UQXTN2 V0.16B, V0.8H - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E214800; // UQXTN2 V0.16B, V0.8H + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("USQADD , ")] - public void Usqadd_S_B_H_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + public void Usqadd_S_B_H_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { - uint Opcode = 0x7E203800; // USQADD B0, B0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x7E203800; // USQADD B0, B0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("USQADD ., .")] - public void Usqadd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + public void Usqadd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E203800; // USQADD V0.8B, V0.8B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E203800; // USQADD V0.8B, V0.8B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("USQADD ., .")] - public void Usqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + public void Usqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E203800; // USQADD V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E203800; // USQADD V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("XTN{2} ., .")] - public void Xtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + public void Xtn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x0E212800; // XTN V0.8B, V0.8H - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E212800; // XTN V0.8B, V0.8H + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("XTN{2} ., .")] - public void Xtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + public void Xtn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x4E212800; // XTN2 V0.16B, V0.8H - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E212800; // XTN2 V0.16B, V0.8H + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs index 7a67d53b91..8f39e49220 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -3,144 +3,11 @@ using ChocolArm64.State; using NUnit.Framework; using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; namespace Ryujinx.Tests.Cpu { public class CpuTestSimdArithmetic : CpuTest { - [TestCase(0x1E224820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000000000000ul)] // FMAX S0, S1, S2 - [TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)] - [TestCase(0x1E224820u, 0x0000000080000000ul, 0x000000003DCCCCCDul, 0x000000003DCCCCCDul)] - [TestCase(0x1E224820u, 0x000000003DCCCCCDul, 0x000000003C9623B1ul, 0x000000003DCCCCCDul)] - [TestCase(0x1E224820u, 0x000000008BA98D27ul, 0x0000000000000076ul, 0x0000000000000076ul)] - [TestCase(0x1E224820u, 0x00000000807FFFFFul, 0x000000007F7FFFFFul, 0x000000007F7FFFFFul)] - [TestCase(0x1E224820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x000000007F7FFFFFul)] - [TestCase(0x1E224820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)] - [TestCase(0x1E224820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)] - [TestCase(0x1E224820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul)] - [TestCase(0x1E224820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul)] - [TestCase(0x1E224820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul)] - [TestCase(0x1E624820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x0000000000000000ul)] // FMAX D0, D1, D2 - [TestCase(0x1E624820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x1E624820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] - [TestCase(0x1E624820u, 0x8000000000000000ul, 0x3FF3333333333333ul, 0x3FF3333333333333ul)] - public void Fmax_S(uint Opcode, ulong A, ulong B, ulong Result) - { - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); - - CompareAgainstUnicorn(); - } - - [TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u)] - [TestCase(0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u)] - [TestCase(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)] - [TestCase(0x80000000u, 0x80000000u, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x3DCCCCCDu)] - [TestCase(0x3DCCCCCDu, 0x3DCCCCCDu, 0x3C9623B1u, 0x3C9623B1u, 0x3DCCCCCDu, 0x3DCCCCCDu)] - [TestCase(0x8BA98D27u, 0x8BA98D27u, 0x00000076u, 0x00000076u, 0x00000076u, 0x00000076u)] - [TestCase(0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)] - [TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)] - [TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u)] - [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u)] - [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au)] - public void Fmax_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1) - { - uint Opcode = 0x4E22F420; // FMAX V0.4S, V1.4S, V2.4S - - Vector128 V1 = MakeVectorE0E1(A, B); - Vector128 V2 = MakeVectorE0E1(C, D); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.Multiple(() => - { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); - }); - - CompareAgainstUnicorn(); - } - - [TestCase(0x1E225820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000080000000ul)] // FMIN S0, S1, S2 - [TestCase(0x1E225820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000080000000ul)] - [TestCase(0x1E225820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)] - [TestCase(0x1E225820u, 0x0000000080000000ul, 0x000000003DCCCCCDul, 0x0000000080000000ul)] - [TestCase(0x1E225820u, 0x000000003DCCCCCDul, 0x000000003C9623B1ul, 0x000000003C9623B1ul)] - [TestCase(0x1E225820u, 0x000000008BA98D27ul, 0x0000000000000076ul, 0x000000008BA98D27ul)] - [TestCase(0x1E225820u, 0x00000000807FFFFFul, 0x000000007F7FFFFFul, 0x00000000807FFFFFul)] - [TestCase(0x1E225820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x00000000807FFFFFul)] - [TestCase(0x1E225820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)] - [TestCase(0x1E225820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)] - [TestCase(0x1E225820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul)] - [TestCase(0x1E225820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul)] - [TestCase(0x1E225820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul)] - [TestCase(0x1E625820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] // FMIN D0, D1, D2 - [TestCase(0x1E625820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x8000000000000000ul)] - [TestCase(0x1E625820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] - [TestCase(0x1E625820u, 0x8000000000000000ul, 0x3FF3333333333333ul, 0x8000000000000000ul)] - public void Fmin_S(uint Opcode, ulong A, ulong B, ulong Result) - { - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); - - CompareAgainstUnicorn(); - } - - [TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u)] - [TestCase(0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)] - [TestCase(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)] - [TestCase(0x80000000u, 0x80000000u, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x80000000u, 0x80000000u)] - [TestCase(0x3DCCCCCDu, 0x3DCCCCCDu, 0x3C9623B1u, 0x3C9623B1u, 0x3C9623B1u, 0x3C9623B1u)] - [TestCase(0x8BA98D27u, 0x8BA98D27u, 0x00000076u, 0x00000076u, 0x8BA98D27u, 0x8BA98D27u)] - [TestCase(0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu)] - [TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu)] - [TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u)] - [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u)] - [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au)] - public void Fmin_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1) - { - uint Opcode = 0x4EA2F420; // FMIN V0.4S, V1.4S, V2.4S - - Vector128 V1 = MakeVectorE0E1(A, B); - Vector128 V2 = MakeVectorE0E1(C, D); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.Multiple(() => - { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FMUL S6, S1, V0.S[2]")] - public void Fmul_Se([Random(10)] float A, [Random(10)] float B) - { - AThreadState ThreadState = SingleOpcode(0x5F809826, - V1: Sse.SetVector128(0, 0, 0, A), - V0: Sse.SetVector128(0, B, 0, 0)); - - Assert.That(Sse41.Extract(ThreadState.V6, (byte)0), Is.EqualTo(A * B)); - - CompareAgainstUnicorn(); - } - [TestCase(0x00000000u, 0x7F800000u)] [TestCase(0x80000000u, 0xFF800000u)] [TestCase(0x00FFF000u, 0x7E000000u)] @@ -148,47 +15,15 @@ namespace Ryujinx.Tests.Cpu [TestCase(0xC1200000u, 0xBDCC8000u)] [TestCase(0x001FFFFFu, 0x7F800000u)] [TestCase(0x007FF000u, 0x7E800000u)] - public void Frecpe_S(uint A, uint Result) + public void Frecpe_S(uint a, uint result) { - uint Opcode = 0x5EA1D820; // FRECPE S0, S1 + uint opcode = 0x5EA1D820; // FRECPE S0, S1 - Vector128 V1 = MakeVectorE0(A); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1); - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); - - CompareAgainstUnicorn(); - } - - [Test, Description("FRECPS D0, D1, D2"), Ignore("Not accurate enough.")] - public void Frecps_S([Random(10)] double A, [Random(10)] double B) - { - AThreadState ThreadState = SingleOpcode(0x5E62FC20, - V1: MakeVectorE0(A), - V2: MakeVectorE0(B)); - - Assert.That(VectorExtractDouble(ThreadState.V0, (byte)0), Is.EqualTo(2 - (A * B))); - - CompareAgainstUnicorn(); - } - - [Test, Description("FRECPS V4.4S, V2.4S, V0.4S")] - public void Frecps_V([Random(10)] float A, [Random(10)] float B) - { - AThreadState ThreadState = SingleOpcode(0x4E20FC44, - V2: Sse.SetAllVector128(A), - V0: Sse.SetAllVector128(B)); - - float Result = (float)(2 - ((double)A * (double)B)); - - Assert.Multiple(() => - { - Assert.That(Sse41.Extract(ThreadState.V4, (byte)0), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)1), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)2), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)3), Is.EqualTo(Result)); - }); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); CompareAgainstUnicorn(); } @@ -231,21 +66,21 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] - public void Frinta_S(uint A, bool DefaultNaN, uint Result) + public void Frinta_S(uint a, bool defaultNaN, uint result) { - uint Opcode = 0x1E264020; // FRINTA S0, S1 + uint opcode = 0x1E264020; // FRINTA S0, S1 - Vector128 V1 = MakeVectorE0(A); + Vector128 v1 = MakeVectorE0(a); - int FpcrTemp = 0x0; - if(DefaultNaN) + int fpcrTemp = 0x0; + if (defaultNaN) { - FpcrTemp = 0x2000000; + fpcrTemp = 0x2000000; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); CompareAgainstUnicorn(); } @@ -262,22 +97,22 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x2E218820u, 0x7F800000FF800000ul, 0x0000000000000000ul, false, 0x7F800000FF800000ul, 0x0000000000000000ul)] [TestCase(0x2E218820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")] [TestCase(0x2E218820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] - public void Frinta_V(uint Opcode, ulong A, ulong B, bool DefaultNaN, ulong Result0, ulong Result1) + public void Frinta_V(uint opcode, ulong a, ulong b, bool defaultNaN, ulong result0, ulong result1) { - Vector128 V1 = MakeVectorE0E1(A, B); + Vector128 v1 = MakeVectorE0E1(a, b); - int FpcrTemp = 0x0; - if(DefaultNaN) + int fpcrTemp = 0x0; + if (defaultNaN) { - FpcrTemp = 0x2000000; + fpcrTemp = 0x2000000; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result0)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(result1)); }); CompareAgainstUnicorn(); @@ -323,28 +158,28 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7FC00002u, 'P', true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, 'M', true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, 'Z', true, 0x7FC00000u, Ignore = "NaN test.")] - public void Frinti_S(uint A, char RoundType, bool DefaultNaN, uint Result) + public void Frinti_S(uint a, char roundMode, bool defaultNaN, uint result) { - uint Opcode = 0x1E27C020; // FRINTI S0, S1 + uint opcode = 0x1E27C020; // FRINTI S0, S1 - Vector128 V1 = MakeVectorE0(A); + Vector128 v1 = MakeVectorE0(a); - int FpcrTemp = 0x0; - switch(RoundType) + int fpcrTemp = 0x0; + switch(roundMode) { - case 'N': FpcrTemp = 0x0; break; - case 'P': FpcrTemp = 0x400000; break; - case 'M': FpcrTemp = 0x800000; break; - case 'Z': FpcrTemp = 0xC00000; break; + case 'N': fpcrTemp = 0x0; break; + case 'P': fpcrTemp = 0x400000; break; + case 'M': fpcrTemp = 0x800000; break; + case 'Z': fpcrTemp = 0xC00000; break; } - if(DefaultNaN) + if (defaultNaN) { - FpcrTemp |= 1 << 25; + fpcrTemp |= 1 << 25; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); CompareAgainstUnicorn(); } @@ -381,29 +216,29 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'P', true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'M', true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'Z', true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] - public void Frinti_V(uint Opcode, ulong A, ulong B, char RoundType, bool DefaultNaN, ulong Result0, ulong Result1) + public void Frinti_V(uint opcode, ulong a, ulong b, char roundMode, bool defaultNaN, ulong result0, ulong result1) { - Vector128 V1 = MakeVectorE0E1(A, B); + Vector128 v1 = MakeVectorE0E1(a, b); - int FpcrTemp = 0x0; - switch(RoundType) + int fpcrTemp = 0x0; + switch(roundMode) { - case 'N': FpcrTemp = 0x0; break; - case 'P': FpcrTemp = 0x400000; break; - case 'M': FpcrTemp = 0x800000; break; - case 'Z': FpcrTemp = 0xC00000; break; + case 'N': fpcrTemp = 0x0; break; + case 'P': fpcrTemp = 0x400000; break; + case 'M': fpcrTemp = 0x800000; break; + case 'Z': fpcrTemp = 0xC00000; break; } - if(DefaultNaN) + if (defaultNaN) { - FpcrTemp |= 1 << 25; + fpcrTemp |= 1 << 25; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result0)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(result1)); }); CompareAgainstUnicorn(); @@ -447,21 +282,21 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] - public void Frintm_S(uint A, bool DefaultNaN, uint Result) + public void Frintm_S(uint a, bool defaultNaN, uint result) { - uint Opcode = 0x1E254020; // FRINTM S0, S1 + uint opcode = 0x1E254020; // FRINTM S0, S1 - Vector128 V1 = MakeVectorE0(A); + Vector128 v1 = MakeVectorE0(a); - int FpcrTemp = 0x0; - if(DefaultNaN) + int fpcrTemp = 0x0; + if (defaultNaN) { - FpcrTemp = 0x2000000; + fpcrTemp = 0x2000000; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); CompareAgainstUnicorn(); } @@ -474,22 +309,22 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x0E219820u, 0x7F800000FF800000ul, 0x0000000000000000ul, false, 0x7F800000FF800000ul, 0x0000000000000000ul)] [TestCase(0x0E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")] [TestCase(0x0E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] - public void Frintm_V(uint Opcode, ulong A, ulong B, bool DefaultNaN, ulong Result0, ulong Result1) + public void Frintm_V(uint opcode, ulong a, ulong b, bool defaultNaN, ulong result0, ulong result1) { - Vector128 V1 = MakeVectorE0E1(A, B); + Vector128 v1 = MakeVectorE0E1(a, b); - int FpcrTemp = 0x0; - if(DefaultNaN) + int fpcrTemp = 0x0; + if (defaultNaN) { - FpcrTemp = 0x2000000; + fpcrTemp = 0x2000000; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result0)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(result1)); }); CompareAgainstUnicorn(); @@ -534,21 +369,21 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] - public void Frintn_S(uint A, bool DefaultNaN, uint Result) + public void Frintn_S(uint a, bool defaultNaN, uint result) { - uint Opcode = 0x1E264020; // FRINTA S0, S1 + uint opcode = 0x1E264020; // FRINTA S0, S1 - Vector128 V1 = MakeVectorE0(A); + Vector128 v1 = MakeVectorE0(a); - int FpcrTemp = 0x0; - if(DefaultNaN) + int fpcrTemp = 0x0; + if (defaultNaN) { - FpcrTemp = 0x2000000; + fpcrTemp = 0x2000000; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); CompareAgainstUnicorn(); } @@ -564,22 +399,22 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x0E218820u, 0x7F800000FF800000ul, 0x0000000000000000ul, false, 0x7F800000FF800000ul, 0x0000000000000000ul)] [TestCase(0x0E218820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")] [TestCase(0x0E218820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] - public void Frintn_V(uint Opcode, ulong A, ulong B, bool DefaultNaN, ulong Result0, ulong Result1) + public void Frintn_V(uint opcode, ulong a, ulong b, bool defaultNaN, ulong result0, ulong result1) { - Vector128 V1 = MakeVectorE0E1(A, B); + Vector128 v1 = MakeVectorE0E1(a, b); - int FpcrTemp = 0x0; - if(DefaultNaN) + int fpcrTemp = 0x0; + if (defaultNaN) { - FpcrTemp = 0x2000000; + fpcrTemp = 0x2000000; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result0)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(result1)); }); CompareAgainstUnicorn(); @@ -623,21 +458,21 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, true, 0x7FC00000u, Ignore = "NaN test.")] - public void Frintp_S(uint A, bool DefaultNaN, uint Result) + public void Frintp_S(uint a, bool defaultNaN, uint result) { - uint Opcode = 0x1E24C020; // FRINTP S0, S1 + uint opcode = 0x1E24C020; // FRINTP S0, S1 - Vector128 V1 = MakeVectorE0(A); + Vector128 v1 = MakeVectorE0(a); - int FpcrTemp = 0x0; - if(DefaultNaN) + int fpcrTemp = 0x0; + if (defaultNaN) { - FpcrTemp = 0x2000000; + fpcrTemp = 0x2000000; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); CompareAgainstUnicorn(); } @@ -650,22 +485,22 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x0EA18820u, 0x7F800000FF800000ul, 0x0000000000000000ul, false, 0x7F800000FF800000ul, 0x0000000000000000ul)] [TestCase(0x0EA18820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")] [TestCase(0x0EA18820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] - public void Frintp_V(uint Opcode, ulong A, ulong B, bool DefaultNaN, ulong Result0, ulong Result1) + public void Frintp_V(uint opcode, ulong a, ulong b, bool defaultNaN, ulong result0, ulong result1) { - Vector128 V1 = MakeVectorE0E1(A, B); + Vector128 v1 = MakeVectorE0E1(a, b); - int FpcrTemp = 0x0; - if(DefaultNaN) + int fpcrTemp = 0x0; + if (defaultNaN) { - FpcrTemp = 0x2000000; + fpcrTemp = 0x2000000; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result0)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(result1)); }); CompareAgainstUnicorn(); @@ -711,28 +546,28 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7FC00002u, 'P', true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, 'M', true, 0x7FC00000u, Ignore = "NaN test.")] [TestCase(0x7FC00002u, 'Z', true, 0x7FC00000u, Ignore = "NaN test.")] - public void Frintx_S(uint A, char RoundType, bool DefaultNaN, uint Result) + public void Frintx_S(uint a, char roundMode, bool defaultNaN, uint result) { - uint Opcode = 0x1E274020; // FRINTX S0, S1 + uint opcode = 0x1E274020; // FRINTX S0, S1 - Vector128 V1 = MakeVectorE0(A); + Vector128 v1 = MakeVectorE0(a); - int FpcrTemp = 0x0; - switch(RoundType) + int fpcrTemp = 0x0; + switch(roundMode) { - case 'N': FpcrTemp = 0x0; break; - case 'P': FpcrTemp = 0x400000; break; - case 'M': FpcrTemp = 0x800000; break; - case 'Z': FpcrTemp = 0xC00000; break; + case 'N': fpcrTemp = 0x0; break; + case 'P': fpcrTemp = 0x400000; break; + case 'M': fpcrTemp = 0x800000; break; + case 'Z': fpcrTemp = 0xC00000; break; } - if(DefaultNaN) + if (defaultNaN) { - FpcrTemp |= 1 << 25; + fpcrTemp |= 1 << 25; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); CompareAgainstUnicorn(); } @@ -769,44 +604,44 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'P', true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'M', true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'Z', true, 0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")] - public void Frintx_V(uint Opcode, ulong A, ulong B, char RoundType, bool DefaultNaN, ulong Result0, ulong Result1) + public void Frintx_V(uint opcode, ulong a, ulong b, char roundMode, bool defaultNaN, ulong result0, ulong result1) { - Vector128 V1 = MakeVectorE0E1(A, B); + Vector128 v1 = MakeVectorE0E1(a, b); - int FpcrTemp = 0x0; - switch(RoundType) + int fpcrTemp = 0x0; + switch(roundMode) { - case 'N': FpcrTemp = 0x0; break; - case 'P': FpcrTemp = 0x400000; break; - case 'M': FpcrTemp = 0x800000; break; - case 'Z': FpcrTemp = 0xC00000; break; + case 'N': fpcrTemp = 0x0; break; + case 'P': fpcrTemp = 0x400000; break; + case 'M': fpcrTemp = 0x800000; break; + case 'Z': fpcrTemp = 0xC00000; break; } - if(DefaultNaN) + if (defaultNaN) { - FpcrTemp |= 1 << 25; + fpcrTemp |= 1 << 25; } - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1, fpcr: fpcrTemp); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result0)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(result1)); }); CompareAgainstUnicorn(); } [TestCase(0x41200000u, 0x3EA18000u)] - public void Frsqrte_S(uint A, uint Result) + public void Frsqrte_S(uint a, uint result) { - uint Opcode = 0x7EA1D820; // FRSQRTE S0, S1 + uint opcode = 0x7EA1D820; // FRSQRTE S0, S1 - Vector128 V1 = MakeVectorE0(A); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + CpuThreadState threadState = SingleOpcode(opcode, v1: v1); - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs deleted file mode 100644 index a1558b05af..0000000000 --- a/Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs +++ /dev/null @@ -1,407 +0,0 @@ -using ChocolArm64.State; - -using NUnit.Framework; - -using System; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace Ryujinx.Tests.Cpu -{ - public class CpuTestSimdCmp : CpuTest - { -#region "ValueSource" - private static float[] _floats_() - { - return new float[] { float.NegativeInfinity, float.MinValue, -1f, -0f, - +0f, +1f, float.MaxValue, float.PositiveInfinity }; - } - - private static double[] _doubles_() - { - return new double[] { double.NegativeInfinity, double.MinValue, -1d, -0d, - +0d, +1d, double.MaxValue, double.PositiveInfinity }; - } -#endregion - - private const int RndCnt = 2; - - [Test, Description("FCMEQ D0, D1, D2 | FCMGE D0, D1, D2 | FCMGT D0, D1, D2")] - public void Fcmeq_Fcmge_Fcmgt_Reg_S_D([ValueSource("_doubles_")] [Random(RndCnt)] double A, - [ValueSource("_doubles_")] [Random(RndCnt)] double B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x5E62E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V0 = Sse.StaticCast(Sse2.SetAllVector128(TestContext.CurrentContext.Random.NextDouble())); - Vector128 V1 = Sse.StaticCast(Sse2.SetScalarVector128(A)); - Vector128 V2 = Sse.StaticCast(Sse2.SetScalarVector128(B)); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMEQ S0, S1, S2 | FCMGE S0, S1, S2 | FCMGT S0, S1, S2")] - public void Fcmeq_Fcmge_Fcmgt_Reg_S_S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [ValueSource("_floats_")] [Random(RndCnt)] float B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x5E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat()); - Vector128 V1 = Sse.SetScalarVector128(A); - Vector128 V2 = Sse.SetScalarVector128(B); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)1), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMEQ V0.2D, V1.2D, V2.2D | FCMGE V0.2D, V1.2D, V2.2D | FCMGT V0.2D, V1.2D, V2.2D")] - public void Fcmeq_Fcmge_Fcmgt_Reg_V_2D([ValueSource("_doubles_")] [Random(RndCnt)] double A, - [ValueSource("_doubles_")] [Random(RndCnt)] double B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x4E62E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V1 = Sse.StaticCast(Sse2.SetAllVector128(A)); - Vector128 V2 = Sse.StaticCast(Sse2.SetAllVector128(B)); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMEQ V0.2S, V1.2S, V2.2S | FCMGE V0.2S, V1.2S, V2.2S | FCMGT V0.2S, V1.2S, V2.2S")] - public void Fcmeq_Fcmge_Fcmgt_Reg_V_2S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [ValueSource("_floats_")] [Random(RndCnt)] float B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x0E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat()); - Vector128 V1 = Sse.SetVector128(0, 0, A, A); - Vector128 V2 = Sse.SetVector128(0, 0, B, B); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMEQ V0.4S, V1.4S, V2.4S | FCMGE V0.4S, V1.4S, V2.4S | FCMGT V0.4S, V1.4S, V2.4S")] - public void Fcmeq_Fcmge_Fcmgt_Reg_V_4S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [ValueSource("_floats_")] [Random(RndCnt)] float B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x4E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V1 = Sse.SetAllVector128(A); - Vector128 V2 = Sse.SetAllVector128(B); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp)); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT D0, D1, #0.0 | FCMGE D0, D1, #0.0 | FCMEQ D0, D1, #0.0 | FCMLE D0, D1, #0.0 | FCMLT D0, D1, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_S_D([ValueSource("_doubles_")] [Random(RndCnt)] double A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x5EE0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V0 = Sse.StaticCast(Sse2.SetAllVector128(TestContext.CurrentContext.Random.NextDouble())); - Vector128 V1 = Sse.StaticCast(Sse2.SetScalarVector128(A)); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - - double Zero = +0d; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT S0, S1, #0.0 | FCMGE S0, S1, #0.0 | FCMEQ S0, S1, #0.0 | FCMLE S0, S1, #0.0 | FCMLT S0, S1, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_S_S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x5EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat()); - Vector128 V1 = Sse.SetScalarVector128(A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - - float Zero = +0f; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)1), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT V0.2D, V1.2D, #0.0 | FCMGE V0.2D, V1.2D, #0.0 | FCMEQ V0.2D, V1.2D, #0.0 | FCMLE V0.2D, V1.2D, #0.0 | FCMLT V0.2D, V1.2D, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_2D([ValueSource("_doubles_")] [Random(RndCnt)] double A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x4EE0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V1 = Sse.StaticCast(Sse2.SetAllVector128(A)); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); - - double Zero = +0d; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT V0.2S, V1.2S, #0.0 | FCMGE V0.2S, V1.2S, #0.0 | FCMEQ V0.2S, V1.2S, #0.0 | FCMLE V0.2S, V1.2S, #0.0 | FCMLT V0.2S, V1.2S, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_2S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x0EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat()); - Vector128 V1 = Sse.SetVector128(0, 0, A, A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - - float Zero = +0f; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT V0.4S, V1.4S, #0.0 | FCMGE V0.4S, V1.4S, #0.0 | FCMEQ V0.4S, V1.4S, #0.0 | FCMLE V0.4S, V1.4S, #0.0 | FCMLT V0.4S, V1.4S, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_4S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x4EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V1 = Sse.SetAllVector128(A); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); - - float Zero = +0f; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp)); - }); - - CompareAgainstUnicorn(); - } - } -} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs index 4efd8f31a2..4702b986d0 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdCrypto.cs @@ -11,98 +11,98 @@ namespace Ryujinx.Tests.Cpu public class CpuTestSimdCrypto : CpuTest { [Test, Description("AESD .16B, .16B")] - public void Aesd_V([Values(0u)] uint Rd, - [Values(1u)] uint Rn, - [Values(0x7B5B546573745665ul)] ulong ValueH, - [Values(0x63746F725D53475Dul)] ulong ValueL, - [Random(2)] ulong RoundKeyH, - [Random(2)] ulong RoundKeyL, - [Values(0x8DCAB9BC035006BCul)] ulong ResultH, - [Values(0x8F57161E00CAFD8Dul)] ulong ResultL) + public void Aesd_V([Values(0u)] uint rd, + [Values(1u)] uint rn, + [Values(0x7B5B546573745665ul)] ulong valueH, + [Values(0x63746F725D53475Dul)] ulong valueL, + [Random(2)] ulong roundKeyH, + [Random(2)] ulong roundKeyL, + [Values(0x8DCAB9BC035006BCul)] ulong resultH, + [Values(0x8F57161E00CAFD8Dul)] ulong resultL) { - uint Opcode = 0x4E285800; // AESD V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4E285800; // AESD V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(RoundKeyL ^ ValueL, RoundKeyH ^ ValueH); - Vector128 V1 = MakeVectorE0E1(RoundKeyL, RoundKeyH); + Vector128 v0 = MakeVectorE0E1(roundKeyL ^ valueL, roundKeyH ^ valueH); + Vector128 v1 = MakeVectorE0E1(roundKeyL, roundKeyH); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + CpuThreadState threadState = SingleOpcode(opcode, v0: v0, v1: v1); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(ResultL)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(ResultH)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(resultL)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(resultH)); }); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(RoundKeyL)); - Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(RoundKeyH)); + Assert.That(GetVectorE0(threadState.V1), Is.EqualTo(roundKeyL)); + Assert.That(GetVectorE1(threadState.V1), Is.EqualTo(roundKeyH)); }); CompareAgainstUnicorn(); } [Test, Description("AESE .16B, .16B")] - public void Aese_V([Values(0u)] uint Rd, - [Values(1u)] uint Rn, - [Values(0x7B5B546573745665ul)] ulong ValueH, - [Values(0x63746F725D53475Dul)] ulong ValueL, - [Random(2)] ulong RoundKeyH, - [Random(2)] ulong RoundKeyL, - [Values(0x8F92A04DFBED204Dul)] ulong ResultH, - [Values(0x4C39B1402192A84Cul)] ulong ResultL) + public void Aese_V([Values(0u)] uint rd, + [Values(1u)] uint rn, + [Values(0x7B5B546573745665ul)] ulong valueH, + [Values(0x63746F725D53475Dul)] ulong valueL, + [Random(2)] ulong roundKeyH, + [Random(2)] ulong roundKeyL, + [Values(0x8F92A04DFBED204Dul)] ulong resultH, + [Values(0x4C39B1402192A84Cul)] ulong resultL) { - uint Opcode = 0x4E284800; // AESE V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4E284800; // AESE V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(RoundKeyL ^ ValueL, RoundKeyH ^ ValueH); - Vector128 V1 = MakeVectorE0E1(RoundKeyL, RoundKeyH); + Vector128 v0 = MakeVectorE0E1(roundKeyL ^ valueL, roundKeyH ^ valueH); + Vector128 v1 = MakeVectorE0E1(roundKeyL, roundKeyH); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + CpuThreadState threadState = SingleOpcode(opcode, v0: v0, v1: v1); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(ResultL)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(ResultH)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(resultL)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(resultH)); }); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(RoundKeyL)); - Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(RoundKeyH)); + Assert.That(GetVectorE0(threadState.V1), Is.EqualTo(roundKeyL)); + Assert.That(GetVectorE1(threadState.V1), Is.EqualTo(roundKeyH)); }); CompareAgainstUnicorn(); } [Test, Description("AESIMC .16B, .16B")] - public void Aesimc_V([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(0x8DCAB9DC035006BCul)] ulong ValueH, - [Values(0x8F57161E00CAFD8Dul)] ulong ValueL, - [Values(0xD635A667928B5EAEul)] ulong ResultH, - [Values(0xEEC9CC3BC55F5777ul)] ulong ResultL) + public void Aesimc_V([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(0x8DCAB9DC035006BCul)] ulong valueH, + [Values(0x8F57161E00CAFD8Dul)] ulong valueL, + [Values(0xD635A667928B5EAEul)] ulong resultH, + [Values(0xEEC9CC3BC55F5777ul)] ulong resultL) { - uint Opcode = 0x4E287800; // AESIMC V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4E287800; // AESIMC V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V = MakeVectorE0E1(ValueL, ValueH); + Vector128 v = MakeVectorE0E1(valueL, valueH); - AThreadState ThreadState = SingleOpcode( - Opcode, - V0: Rn == 0u ? V : default(Vector128), - V1: Rn == 1u ? V : default(Vector128)); + CpuThreadState threadState = SingleOpcode( + opcode, + v0: rn == 0u ? v : default(Vector128), + v1: rn == 1u ? v : default(Vector128)); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(ResultL)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(ResultH)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(resultL)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(resultH)); }); - if (Rn == 1u) + if (rn == 1u) { Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(ValueL)); - Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(ValueH)); + Assert.That(GetVectorE0(threadState.V1), Is.EqualTo(valueL)); + Assert.That(GetVectorE1(threadState.V1), Is.EqualTo(valueH)); }); } @@ -110,34 +110,34 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("AESMC .16B, .16B")] - public void Aesmc_V([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(0x627A6F6644B109C8ul)] ulong ValueH, - [Values(0x2B18330A81C3B3E5ul)] ulong ValueL, - [Values(0x7B5B546573745665ul)] ulong ResultH, - [Values(0x63746F725D53475Dul)] ulong ResultL) + public void Aesmc_V([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(0x627A6F6644B109C8ul)] ulong valueH, + [Values(0x2B18330A81C3B3E5ul)] ulong valueL, + [Values(0x7B5B546573745665ul)] ulong resultH, + [Values(0x63746F725D53475Dul)] ulong resultL) { - uint Opcode = 0x4E286800; // AESMC V0.16B, V0.16B - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4E286800; // AESMC V0.16B, V0.16B + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V = MakeVectorE0E1(ValueL, ValueH); + Vector128 v = MakeVectorE0E1(valueL, valueH); - AThreadState ThreadState = SingleOpcode( - Opcode, - V0: Rn == 0u ? V : default(Vector128), - V1: Rn == 1u ? V : default(Vector128)); + CpuThreadState threadState = SingleOpcode( + opcode, + v0: rn == 0u ? v : default(Vector128), + v1: rn == 1u ? v : default(Vector128)); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(ResultL)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(ResultH)); + Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(resultL)); + Assert.That(GetVectorE1(threadState.V0), Is.EqualTo(resultH)); }); - if (Rn == 1u) + if (rn == 1u) { Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(ValueL)); - Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(ValueH)); + Assert.That(GetVectorE0(threadState.V1), Is.EqualTo(valueL)); + Assert.That(GetVectorE1(threadState.V1), Is.EqualTo(valueH)); }); } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs deleted file mode 100644 index 3c8ad0711c..0000000000 --- a/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs +++ /dev/null @@ -1,43 +0,0 @@ -using ChocolArm64.State; - -using NUnit.Framework; - -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace Ryujinx.Tests.Cpu -{ - public class CpuTestSimdCvt : CpuTest - { - [TestCase((ushort)0x0000, 0x00000000u)] // Positive Zero - [TestCase((ushort)0x8000, 0x80000000u)] // Negative Zero - [TestCase((ushort)0x3E00, 0x3FC00000u)] // +1.5 - [TestCase((ushort)0xBE00, 0xBFC00000u)] // -1.5 - [TestCase((ushort)0xFFFF, 0xFFFFE000u)] // -QNaN - [TestCase((ushort)0x7C00, 0x7F800000u)] // +Inf - [TestCase((ushort)0x3C00, 0x3F800000u)] // 1.0 - [TestCase((ushort)0x3C01, 0x3F802000u)] // 1.0009765625 - [TestCase((ushort)0xC000, 0xC0000000u)] // -2.0 - [TestCase((ushort)0x7BFF, 0x477FE000u)] // 65504.0 (Largest Normal) - [TestCase((ushort)0x03FF, 0x387FC000u)] // 0.00006097555 (Largest Subnormal) - [TestCase((ushort)0x0001, 0x33800000u)] // 5.96046448e-8 (Smallest Subnormal) - public void Fcvtl_V_f16(ushort Value, uint Result) - { - uint Opcode = 0x0E217801; // FCVTL V1.4S, V0.4H - - Vector128 V0 = Sse.StaticCast(Sse2.SetAllVector128(Value)); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0); - - Assert.Multiple(() => - { - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V1), (byte)0), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V1), (byte)1), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V1), (byte)2), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V1), (byte)3), Is.EqualTo(Result)); - }); - - CompareAgainstUnicorn(); - } - } -} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs b/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs new file mode 100644 index 0000000000..f232989f77 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs @@ -0,0 +1,73 @@ +#define SimdExt + +using NUnit.Framework; + +using System.Runtime.Intrinsics; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdExt")] + public sealed class CpuTestSimdExt : CpuTest + { +#if SimdExt + +#region "ValueSource" + private static ulong[] _8B_() + { + return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + } +#endregion + + private const int RndCnt = 2; + + [Test, Pairwise, Description("EXT .8B, .8B, .8B, #")] + public void Ext_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b, + [Range(0u, 7u)] uint index) + { + uint imm4 = index & 0x7u; + + uint opcode = 0x2E000000; // EXT V0.8B, V0.8B, V0.8B, #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm4 << 11); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("EXT .16B, .16B, .16B, #")] + public void Ext_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b, + [Range(0u, 15u)] uint index) + { + uint imm4 = index & 0xFu; + + uint opcode = 0x6E000000; // EXT V0.16B, V0.16B, V0.16B, #0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm4 << 11); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); + + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } +#endif + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs b/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs new file mode 100644 index 0000000000..48efc18fd2 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdFcond.cs @@ -0,0 +1,178 @@ +#define SimdFcond + +using NUnit.Framework; + +using System.Collections.Generic; +using System.Runtime.Intrinsics; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdFcond")] + public sealed class CpuTestSimdFcond : CpuTest + { +#if SimdFcond + +#region "ValueSource (Types)" + private static IEnumerable _1S_F_() + { + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x0000000080800000ul; // -Min Normal + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0000000000800000ul; // +Min Normal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x0000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0x00000000FF800000ul; // -Infinity + yield return 0x000000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong grbg = TestContext.CurrentContext.Random.NextUInt(); + ulong rnd1 = GenNormalS(); + ulong rnd2 = GenSubnormalS(); + + yield return (grbg << 32) | rnd1; + yield return (grbg << 32) | rnd2; + } + } + + private static IEnumerable _1D_F_() + { + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = GenNormalD(); + ulong rnd2 = GenSubnormalD(); + + yield return rnd1; + yield return rnd2; + } + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _F_Ccmp_Ccmpe_S_S_() + { + return new uint[] + { + 0x1E220420u, // FCCMP S1, S2, #0, EQ + 0x1E220430u // FCCMPE S1, S2, #0, EQ + }; + } + + private static uint[] _F_Ccmp_Ccmpe_S_D_() + { + return new uint[] + { + 0x1E620420u, // FCCMP D1, D2, #0, EQ + 0x1E620430u // FCCMPE D1, D2, #0, EQ + }; + } +#endregion + + private const int RndCnt = 2; + private const int RndCntNzcv = 2; + + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + + [Test, Pairwise] [Explicit] + public void F_Ccmp_Ccmpe_S_S([ValueSource("_F_Ccmp_Ccmpe_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a, + [ValueSource("_1S_F_")] ulong b, + [Random(0u, 15u, RndCntNzcv)] uint nzcv, + [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // + { + opcodes |= ((cond & 15) << 12) | ((nzcv & 15) << 0); + + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + bool v = TestContext.CurrentContext.Random.NextBool(); + bool c = TestContext.CurrentContext.Random.NextBool(); + bool z = TestContext.CurrentContext.Random.NextBool(); + bool n = TestContext.CurrentContext.Random.NextBool(); + + SingleOpcode(opcodes, v1: v1, v2: v2, overflow: v, carry: c, zero: z, negative: n); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); + } + + [Test, Pairwise] [Explicit] + public void F_Ccmp_Ccmpe_S_D([ValueSource("_F_Ccmp_Ccmpe_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b, + [Random(0u, 15u, RndCntNzcv)] uint nzcv, + [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // + { + opcodes |= ((cond & 15) << 12) | ((nzcv & 15) << 0); + + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + bool v = TestContext.CurrentContext.Random.NextBool(); + bool c = TestContext.CurrentContext.Random.NextBool(); + bool z = TestContext.CurrentContext.Random.NextBool(); + bool n = TestContext.CurrentContext.Random.NextBool(); + + SingleOpcode(opcodes, v1: v1, v2: v2, overflow: v, carry: c, zero: z, negative: n); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); + } +#endif + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs b/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs new file mode 100644 index 0000000000..4ca54a2b42 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs @@ -0,0 +1,377 @@ +#define SimdIns + +using NUnit.Framework; + +using System.Runtime.Intrinsics; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdIns")] + public sealed class CpuTestSimdIns : CpuTest + { +#if SimdIns + +#region "ValueSource" + private static ulong[] _1D_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static ulong[] _2S_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static ulong[] _4H_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static ulong[] _8B_() + { + return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static ulong[] _8B4H_() + { + return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static ulong[] _8B4H2S_() + { + return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static uint[] _W_() + { + return new uint[] { 0x00000000u, 0x0000007Fu, + 0x00000080u, 0x000000FFu, + 0x00007FFFu, 0x00008000u, + 0x0000FFFFu, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu }; + } + + private static ulong[] _X_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + } +#endregion + + private const int RndCnt = 2; + + [Test, Pairwise, Description("DUP ., ")] + public void Dup_Gp_W([Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_W_")] [Random(RndCnt)] uint wn, + [Values(0, 1, 2)] int size, // Q0: <8B, 4H, 2S> + [Values(0b0u, 0b1u)] uint q) // Q1: <16B, 8H, 4S> + { + uint imm5 = (1u << size) & 0x1Fu; + + uint opcode = 0x0E000C00; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + opcode |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + + SingleOpcode(opcode, x1: wn, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP ., ")] + public void Dup_Gp_X([Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_X_")] [Random(RndCnt)] ulong xn) + { + uint opcode = 0x4E080C00; // DUP V0.2D, X0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + + SingleOpcode(opcode, x1: xn, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP B0, V1.B[]")] + public void Dup_S_B([ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [Range(0u, 15u)] uint index) + { + const int size = 0; + + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x5E000420; // RESERVED + opcode |= (imm5 << 16); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP H0, V1.H[]")] + public void Dup_S_H([ValueSource("_4H_")] [Random(RndCnt)] ulong a, + [Range(0u, 7u)] uint index) + { + const int size = 1; + + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x5E000420; // RESERVED + opcode |= (imm5 << 16); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP S0, V1.S[]")] + public void Dup_S_S([ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [Range(0u, 3u)] uint index) + { + const int size = 2; + + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x5E000420; // RESERVED + opcode |= (imm5 << 16); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP D0, V1.D[]")] + public void Dup_S_D([ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(0u, 1u)] uint index) + { + const int size = 3; + + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x5E000420; // RESERVED + opcode |= (imm5 << 16); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP ., .B[]")] + public void Dup_V_8B_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [Range(0u, 15u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + const int size = 0; + + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x0E000400; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + opcode |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP ., .H[]")] + public void Dup_V_4H_8H([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H_")] [Random(RndCnt)] ulong a, + [Range(0u, 7u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <4H, 8H> + { + const int size = 1; + + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x0E000400; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + opcode |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP ., .S[]")] + public void Dup_V_2S_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [Range(0u, 3u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + const int size = 2; + + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x0E000400; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + opcode |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("DUP ., .D[]")] + public void Dup_V_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(0u, 1u)] uint index, + [Values(0b1u)] uint q) // <2D> + { + const int size = 3; + + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x0E000400; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + opcode |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcode, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("SMOV , .[]")] + public void Smov_S_W([Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_8B4H_")] [Random(RndCnt)] ulong a, + [Values(0, 1)] int size, // + [Values(0u, 1u, 2u, 3u)] uint index) + { + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x0E002C00; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + + ulong x0 = (ulong)TestContext.CurrentContext.Random.NextUInt() << 32; + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcode, x0: x0, x31: w31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("SMOV , .[]")] + public void Smov_S_X([Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [Values(0, 1, 2)] int size, // + [Values(0u, 1u)] uint index) + { + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x4E002C00; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcode, x31: x31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("UMOV , .[]")] + public void Umov_S_W([Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [Values(0, 1, 2)] int size, // + [Values(0u, 1u)] uint index) + { + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x0E003C00; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + + ulong x0 = (ulong)TestContext.CurrentContext.Random.NextUInt() << 32; + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcode, x0: x0, x31: w31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("UMOV , .[]")] + public void Umov_S_X([Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Values(3)] int size, // + [Values(0u)] uint index) + { + uint imm5 = (index << (size + 1) | 1u << size) & 0x1Fu; + + uint opcode = 0x4E003C00; // RESERVED + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (imm5 << 16); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcode, x31: x31, v1: v1); + + CompareAgainstUnicorn(); + } +#endif + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index b7150db30a..d43447a7ad 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -1,19 +1,18 @@ #define SimdReg -using ChocolArm64.State; - using NUnit.Framework; +using System.Collections.Generic; using System.Runtime.Intrinsics; namespace Ryujinx.Tests.Cpu { - [Category("SimdReg")] // Tested: second half of 2018. + [Category("SimdReg")] public sealed class CpuTestSimdReg : CpuTest { #if SimdReg -#region "ValueSource" +#region "ValueSource (Types)" private static ulong[] _1B1H1S1D_() { return new ulong[] { 0x0000000000000000ul, 0x000000000000007Ful, @@ -76,2922 +75,3717 @@ namespace Ryujinx.Tests.Cpu 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } + + private static IEnumerable _1S_F_() + { + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x0000000080800000ul; // -Min Normal + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0000000000800000ul; // +Min Normal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x0000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0x00000000FF800000ul; // -Infinity + yield return 0x000000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong grbg = TestContext.CurrentContext.Random.NextUInt(); + ulong rnd1 = GenNormalS(); + ulong rnd2 = GenSubnormalS(); + + yield return (grbg << 32) | rnd1; + yield return (grbg << 32) | rnd2; + } + } + + private static IEnumerable _2S_F_() + { + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x8080000080800000ul; // -Min Normal + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0080000000800000ul; // +Min Normal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFF800000FF800000ul; // -Infinity + yield return 0x7F8000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = GenNormalS(); + ulong rnd2 = GenSubnormalS(); + + yield return (rnd1 << 32) | rnd1; + yield return (rnd2 << 32) | rnd2; + } + } + + private static IEnumerable _1D_F_() + { + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = GenNormalD(); + ulong rnd2 = GenSubnormalD(); + + yield return rnd1; + yield return rnd2; + } + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _F_Add_Div_Mul_Mulx_Sub_S_S_() + { + return new uint[] + { + 0x1E222820u, // FADD S0, S1, S2 + 0x1E221820u, // FDIV S0, S1, S2 + 0x1E220820u, // FMUL S0, S1, S2 + 0x5E22DC20u, // FMULX S0, S1, S2 + 0x1E223820u // FSUB S0, S1, S2 + }; + } + + private static uint[] _F_Add_Div_Mul_Mulx_Sub_S_D_() + { + return new uint[] + { + 0x1E622820u, // FADD D0, D1, D2 + 0x1E621820u, // FDIV D0, D1, D2 + 0x1E620820u, // FMUL D0, D1, D2 + 0x5E62DC20u, // FMULX D0, D1, D2 + 0x1E623820u // FSUB D0, D1, D2 + }; + } + + private static uint[] _F_Add_Div_Mul_Mulx_Sub_V_2S_4S_() + { + return new uint[] + { + 0x0E20D400u, // FADD V0.2S, V0.2S, V0.2S + 0x2E20FC00u, // FDIV V0.2S, V0.2S, V0.2S + 0x2E20DC00u, // FMUL V0.2S, V0.2S, V0.2S + 0x0E20DC00u, // FMULX V0.2S, V0.2S, V0.2S + 0x0EA0D400u // FSUB V0.2S, V0.2S, V0.2S + }; + } + + private static uint[] _F_Add_Div_Mul_Mulx_Sub_V_2D_() + { + return new uint[] + { + 0x4E60D400u, // FADD V0.2D, V0.2D, V0.2D + 0x6E60FC00u, // FDIV V0.2D, V0.2D, V0.2D + 0x6E60DC00u, // FMUL V0.2D, V0.2D, V0.2D + 0x4E60DC00u, // FMULX V0.2D, V0.2D, V0.2D + 0x4EE0D400u // FSUB V0.2D, V0.2D, V0.2D + }; + } + + private static uint[] _F_Cmp_Cmpe_S_S_() + { + return new uint[] + { + 0x1E222020u, // FCMP S1, S2 + 0x1E222030u // FCMPE S1, S2 + }; + } + + private static uint[] _F_Cmp_Cmpe_S_D_() + { + return new uint[] + { + 0x1E622020u, // FCMP D1, D2 + 0x1E622030u // FCMPE D1, D2 + }; + } + + private static uint[] _F_Madd_Msub_S_S_() + { + return new uint[] + { + 0x1F020C20u, // FMADD S0, S1, S2, S3 + 0x1F028C20u // FMSUB S0, S1, S2, S3 + }; + } + + private static uint[] _F_Madd_Msub_S_D_() + { + return new uint[] + { + 0x1F420C20u, // FMADD D0, D1, D2, D3 + 0x1F428C20u // FMSUB D0, D1, D2, D3 + }; + } + + private static uint[] _F_Max_Min_Nm_S_S_() + { + return new uint[] + { + 0x1E224820u, // FMAX S0, S1, S2 + 0x1E226820u, // FMAXNM S0, S1, S2 + 0x1E225820u, // FMIN S0, S1, S2 + 0x1E227820u // FMINNM S0, S1, S2 + }; + } + + private static uint[] _F_Max_Min_Nm_S_D_() + { + return new uint[] + { + 0x1E624820u, // FMAX D0, D1, D2 + 0x1E626820u, // FMAXNM D0, D1, D2 + 0x1E625820u, // FMIN D0, D1, D2 + 0x1E627820u // FMINNM D0, D1, D2 + }; + } + + private static uint[] _F_Max_Min_Nm_P_V_2S_4S_() + { + return new uint[] + { + 0x0E20F400u, // FMAX V0.2S, V0.2S, V0.2S + 0x0E20C400u, // FMAXNM V0.2S, V0.2S, V0.2S + 0x2E20F400u, // FMAXP V0.2S, V0.2S, V0.2S + 0x0EA0F400u, // FMIN V0.2S, V0.2S, V0.2S + 0x0EA0C400u, // FMINNM V0.2S, V0.2S, V0.2S + 0x2EA0F400u // FMINP V0.2S, V0.2S, V0.2S + }; + } + + private static uint[] _F_Max_Min_Nm_P_V_2D_() + { + return new uint[] + { + 0x4E60F400u, // FMAX V0.2D, V0.2D, V0.2D + 0x4E60C400u, // FMAXNM V0.2D, V0.2D, V0.2D + 0x6E60F400u, // FMAXP V0.2D, V0.2D, V0.2D + 0x4EE0F400u, // FMIN V0.2D, V0.2D, V0.2D + 0x4EE0C400u, // FMINNM V0.2D, V0.2D, V0.2D + 0x6EE0F400u // FMINP V0.2D, V0.2D, V0.2D + }; + } + + private static uint[] _F_Mla_Mls_V_2S_4S_() + { + return new uint[] + { + 0x0E20CC00u, // FMLA V0.2S, V0.2S, V0.2S + 0x0EA0CC00u // FMLS V0.2S, V0.2S, V0.2S + }; + } + + private static uint[] _F_Mla_Mls_V_2D_() + { + return new uint[] + { + 0x4E60CC00u, // FMLA V0.2D, V0.2D, V0.2D + 0x4EE0CC00u // FMLS V0.2D, V0.2D, V0.2D + }; + } + + private static uint[] _F_Recps_Rsqrts_S_S_() + { + return new uint[] + { + 0x5E22FC20u, // FRECPS S0, S1, S2 + 0x5EA2FC20u // FRSQRTS S0, S1, S2 + }; + } + + private static uint[] _F_Recps_Rsqrts_S_D_() + { + return new uint[] + { + 0x5E62FC20u, // FRECPS D0, D1, D2 + 0x5EE2FC20u // FRSQRTS D0, D1, D2 + }; + } + + private static uint[] _F_Recps_Rsqrts_V_2S_4S_() + { + return new uint[] + { + 0x0E20FC00u, // FRECPS V0.2S, V0.2S, V0.2S + 0x0EA0FC00u // FRSQRTS V0.2S, V0.2S, V0.2S + }; + } + + private static uint[] _F_Recps_Rsqrts_V_2D_() + { + return new uint[] + { + 0x4E60FC00u, // FRECPS V0.2D, V0.2D, V0.2D + 0x4EE0FC00u // FRSQRTS V0.2D, V0.2D, V0.2D + }; + } + + private static uint[] _Sha1c_Sha1m_Sha1p_Sha1su0_V_() + { + return new uint[] + { + 0x5E000000u, // SHA1C Q0, S0, V0.4S + 0x5E002000u, // SHA1M Q0, S0, V0.4S + 0x5E001000u, // SHA1P Q0, S0, V0.4S + 0x5E003000u // SHA1SU0 V0.4S, V0.4S, V0.4S + }; + } + + private static uint[] _Sha256h_Sha256h2_Sha256su1_V_() + { + return new uint[] + { + 0x5E004000u, // SHA256H Q0, Q0, V0.4S + 0x5E005000u, // SHA256H2 Q0, Q0, V0.4S + 0x5E006000u // SHA256SU1 V0.4S, V0.4S, V0.4S + }; + } + + private static uint[] _S_Max_Min_P_V_() + { + return new uint[] + { + 0x0E206400u, // SMAX V0.8B, V0.8B, V0.8B + 0x0E20A400u, // SMAXP V0.8B, V0.8B, V0.8B + 0x0E206C00u, // SMIN V0.8B, V0.8B, V0.8B + 0x0E20AC00u // SMINP V0.8B, V0.8B, V0.8B + }; + } + + private static uint[] _U_Max_Min_P_V_() + { + return new uint[] + { + 0x2E206400u, // UMAX V0.8B, V0.8B, V0.8B + 0x2E20A400u, // UMAXP V0.8B, V0.8B, V0.8B + 0x2E206C00u, // UMIN V0.8B, V0.8B, V0.8B + 0x2E20AC00u // UMINP V0.8B, V0.8B, V0.8B + }; + } #endregion private const int RndCnt = 2; + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + [Test, Pairwise, Description("ADD , , ")] - public void Add_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + public void Add_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x5EE08400; // ADD D0, D0, D0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EE08400; // ADD D0, D0, D0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD ., ., .")] - public void Add_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Add_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E208400; // ADD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E208400; // ADD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADD ., ., .")] - public void Add_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Add_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E208400; // ADD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E208400; // ADD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDHN{2} ., ., .")] - public void Addhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + public void Addhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x0E204000; // ADDHN V0.8B, V0.8H, V0.8H - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E204000; // ADDHN V0.8B, V0.8H, V0.8H + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDHN{2} ., ., .")] - public void Addhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + public void Addhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x4E204000; // ADDHN2 V0.16B, V0.8H, V0.8H - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E204000; // ADDHN2 V0.16B, V0.8H, V0.8H + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDP ., ., .")] - public void Addp_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Addp_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E20BC00; // ADDP V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E20BC00; // ADDP V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ADDP ., ., .")] - public void Addp_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Addp_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E20BC00; // ADDP V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E20BC00; // ADDP V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("AND ., ., .")] - public void And_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void And_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x0E201C00; // AND V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x0E201C00; // AND V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("AND ., ., .")] - public void And_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void And_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x4E201C00; // AND V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4E201C00; // AND V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BIC ., ., .")] - public void Bic_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Bic_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x0E601C00; // BIC V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x0E601C00; // BIC V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BIC ., ., .")] - public void Bic_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Bic_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x4E601C00; // BIC V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4E601C00; // BIC V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BIF ., ., .")] - public void Bif_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Bif_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x2EE01C00; // BIF V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x2EE01C00; // BIF V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BIF ., ., .")] - public void Bif_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Bif_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x6EE01C00; // BIF V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x6EE01C00; // BIF V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BIT ., ., .")] - public void Bit_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Bit_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x2EA01C00; // BIT V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x2EA01C00; // BIT V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BIT ., ., .")] - public void Bit_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Bit_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x6EA01C00; // BIT V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x6EA01C00; // BIT V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BSL ., ., .")] - public void Bsl_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Bsl_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x2E601C00; // BSL V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x2E601C00; // BSL V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("BSL ., ., .")] - public void Bsl_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Bsl_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x6E601C00; // BSL V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x6E601C00; // BSL V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMEQ , , ")] - public void Cmeq_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + public void Cmeq_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x7EE08C00; // CMEQ D0, D0, D0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x7EE08C00; // CMEQ D0, D0, D0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMEQ ., ., .")] - public void Cmeq_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Cmeq_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E208C00; // CMEQ V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E208C00; // CMEQ V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMEQ ., ., .")] - public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E208C00; // CMEQ V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E208C00; // CMEQ V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGE , , ")] - public void Cmge_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + public void Cmge_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x5EE03C00; // CMGE D0, D0, D0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EE03C00; // CMGE D0, D0, D0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGE ., ., .")] - public void Cmge_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Cmge_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E203C00; // CMGE V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E203C00; // CMGE V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGE ., ., .")] - public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E203C00; // CMGE V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E203C00; // CMGE V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGT , , ")] - public void Cmgt_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + public void Cmgt_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x5EE03400; // CMGT D0, D0, D0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EE03400; // CMGT D0, D0, D0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGT ., ., .")] - public void Cmgt_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Cmgt_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E203400; // CMGT V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E203400; // CMGT V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMGT ., ., .")] - public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E203400; // CMGT V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E203400; // CMGT V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMHI , , ")] - public void Cmhi_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + public void Cmhi_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x7EE03400; // CMHI D0, D0, D0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x7EE03400; // CMHI D0, D0, D0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMHI ., ., .")] - public void Cmhi_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Cmhi_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E203400; // CMHI V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E203400; // CMHI V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMHI ., ., .")] - public void Cmhi_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Cmhi_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E203400; // CMHI V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E203400; // CMHI V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMHS , , ")] - public void Cmhs_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + public void Cmhs_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x7EE03C00; // CMHS D0, D0, D0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x7EE03C00; // CMHS D0, D0, D0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMHS ., ., .")] - public void Cmhs_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Cmhs_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E203C00; // CMHS V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E203C00; // CMHS V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMHS ., ., .")] - public void Cmhs_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Cmhs_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E203C00; // CMHS V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E203C00; // CMHS V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMTST , , ")] - public void Cmtst_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + public void Cmtst_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x5EE08C00; // CMTST D0, D0, D0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x5EE08C00; // CMTST D0, D0, D0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMTST ., ., .")] - public void Cmtst_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Cmtst_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E208C00; // CMTST V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E208C00; // CMTST V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("CMTST ., ., .")] - public void Cmtst_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Cmtst_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E208C00; // CMTST V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E208C00; // CMTST V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EOR ., ., .")] - public void Eor_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Eor_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x2E201C00; // EOR V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x2E201C00; // EOR V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("EOR ., ., .")] - public void Eor_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Eor_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x6E201C00; // EOR V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x6E201C00; // EOR V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Add_Div_Mul_Mulx_Sub_S_S([ValueSource("_F_Add_Div_Mul_Mulx_Sub_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a, + [ValueSource("_1S_F_")] ulong b) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Add_Div_Mul_Mulx_Sub_S_D([ValueSource("_F_Add_Div_Mul_Mulx_Sub_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Add_Div_Mul_Mulx_Sub_V_2S_4S([ValueSource("_F_Add_Div_Mul_Mulx_Sub_V_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [ValueSource("_2S_F_")] ulong b, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * q); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Add_Div_Mul_Mulx_Sub_V_2D([ValueSource("_F_Add_Div_Mul_Mulx_Sub_V_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b) + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Dzc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Cmp_Cmpe_S_S([ValueSource("_F_Cmp_Cmpe_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a, + [ValueSource("_1S_F_")] ulong b) + { + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + bool v = TestContext.CurrentContext.Random.NextBool(); + bool c = TestContext.CurrentContext.Random.NextBool(); + bool z = TestContext.CurrentContext.Random.NextBool(); + bool n = TestContext.CurrentContext.Random.NextBool(); + + SingleOpcode(opcodes, v1: v1, v2: v2, overflow: v, carry: c, zero: z, negative: n); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); + } + + [Test, Pairwise] [Explicit] + public void F_Cmp_Cmpe_S_D([ValueSource("_F_Cmp_Cmpe_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b) + { + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + bool v = TestContext.CurrentContext.Random.NextBool(); + bool c = TestContext.CurrentContext.Random.NextBool(); + bool z = TestContext.CurrentContext.Random.NextBool(); + bool n = TestContext.CurrentContext.Random.NextBool(); + + SingleOpcode(opcodes, v1: v1, v2: v2, overflow: v, carry: c, zero: z, negative: n); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Madd_Msub_S_S([ValueSource("_F_Madd_Msub_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a, + [ValueSource("_1S_F_")] ulong b, + [ValueSource("_1S_F_")] ulong c) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + Vector128 v3 = MakeVectorE0(c); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v3: v3, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Madd_Msub_S_D([ValueSource("_F_Madd_Msub_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b, + [ValueSource("_1D_F_")] ulong c) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + Vector128 v3 = MakeVectorE0(c); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v3: v3, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); + } + + [Test, Pairwise] [Explicit] + public void F_Max_Min_Nm_S_S([ValueSource("_F_Max_Min_Nm_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a, + [ValueSource("_1S_F_")] ulong b) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Max_Min_Nm_S_D([ValueSource("_F_Max_Min_Nm_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Max_Min_Nm_P_V_2S_4S([ValueSource("_F_Max_Min_Nm_P_V_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [ValueSource("_2S_F_")] ulong b, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * q); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Max_Min_Nm_P_V_2D([ValueSource("_F_Max_Min_Nm_P_V_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b) + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_V_2S_4S([ValueSource("_F_Mla_Mls_V_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [ValueSource("_2S_F_")] ulong b, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * q); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_V_2D([ValueSource("_F_Mla_Mls_V_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b) + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Recps_Rsqrts_S_S([ValueSource("_F_Recps_Rsqrts_S_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a, + [ValueSource("_1S_F_")] ulong b) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Recps_Rsqrts_S_D([ValueSource("_F_Recps_Rsqrts_S_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b) + { + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Recps_Rsqrts_V_2S_4S([ValueSource("_F_Recps_Rsqrts_V_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [ValueSource("_2S_F_")] ulong b, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * q); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Recps_Rsqrts_V_2D([ValueSource("_F_Recps_Rsqrts_V_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b) + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); + } + + [Test, Pairwise, Description("ORN ., ., .")] + public void Orn_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) + { + uint opcode = 0x0EE01C00; // ORN V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORN ., ., .")] - public void Orn_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Orn_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x0EE01C00; // ORN V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4EE01C00; // ORN V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - CompareAgainstUnicorn(); - } - - [Test, Pairwise, Description("ORN ., ., .")] - public void Orn_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) - { - uint Opcode = 0x4EE01C00; // ORN V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORR ., ., .")] - public void Orr_V_8B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Orr_V_8B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x0EA01C00; // ORR V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x0EA01C00; // ORR V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ORR ., ., .")] - public void Orr_V_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + public void Orr_V_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x4EA01C00; // ORR V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x4EA01C00; // ORR V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RADDHN{2} ., ., .")] - public void Raddhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + public void Raddhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x2E204000; // RADDHN V0.8B, V0.8H, V0.8H - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E204000; // RADDHN V0.8B, V0.8H, V0.8H + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RADDHN{2} ., ., .")] - public void Raddhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + public void Raddhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x6E204000; // RADDHN2 V0.16B, V0.8H, V0.8H - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E204000; // RADDHN2 V0.16B, V0.8H, V0.8H + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RSUBHN{2} ., ., .")] - public void Rsubhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + public void Rsubhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x2E206000; // RSUBHN V0.8B, V0.8H, V0.8H - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E206000; // RSUBHN V0.8B, V0.8H, V0.8H + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("RSUBHN{2} ., ., .")] - public void Rsubhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + public void Rsubhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x6E206000; // RSUBHN2 V0.16B, V0.8H, V0.8H - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E206000; // RSUBHN2 V0.16B, V0.8H, V0.8H + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SABA ., ., .")] - public void Saba_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Saba_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E207C00; // SABA V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E207C00; // SABA V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SABA ., ., .")] - public void Saba_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Saba_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E207C00; // SABA V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E207C00; // SABA V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SABAL{2} ., ., .")] - public void Sabal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sabal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x0E205000; // SABAL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E205000; // SABAL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SABAL{2} ., ., .")] - public void Sabal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sabal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E205000; // SABAL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E205000; // SABAL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SABD ., ., .")] - public void Sabd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sabd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E207400; // SABD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E207400; // SABD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SABD ., ., .")] - public void Sabd_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sabd_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E207400; // SABD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E207400; // SABD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SABDL{2} ., ., .")] - public void Sabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x0E207000; // SABDL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E207000; // SABDL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SABDL{2} ., ., .")] - public void Sabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E207000; // SABDL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E207000; // SABDL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SADDL{2} ., ., .")] - public void Saddl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Saddl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x0E200000; // SADDL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E200000; // SADDL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SADDL{2} ., ., .")] - public void Saddl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Saddl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E200000; // SADDL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E200000; // SADDL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SADDW{2} ., ., .")] - public void Saddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Saddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { - uint Opcode = 0x0E201000; // SADDW V0.8H, V0.8H, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E201000; // SADDW V0.8H, V0.8H, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SADDW{2} ., ., .")] - public void Saddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Saddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { - uint Opcode = 0x4E201000; // SADDW2 V0.8H, V0.8H, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E201000; // SADDW2 V0.8H, V0.8H, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } - [Test, Pairwise, Description("SHA256H , , .4S")] - public void Sha256h_V([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [Random(RndCnt / 2)] ulong Z0, [Random(RndCnt / 2)] ulong Z1, - [Random(RndCnt / 2)] ulong A0, [Random(RndCnt / 2)] ulong A1, - [Random(RndCnt / 2)] ulong B0, [Random(RndCnt / 2)] ulong B1) + [Test, Pairwise] + public void Sha1c_Sha1m_Sha1p_Sha1su0_V([ValueSource("_Sha1c_Sha1m_Sha1p_Sha1su0_V_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, + [Random(RndCnt / 2)] ulong a0, [Random(RndCnt / 2)] ulong a1, + [Random(RndCnt / 2)] ulong b0, [Random(RndCnt / 2)] ulong b1) { - uint Opcode = 0x5E004000; // SHA256H Q0, Q0, V0.4S - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z0, Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 v0 = MakeVectorE0E1(z0, z1); + Vector128 v1 = MakeVectorE0E1(a0, a1); + Vector128 v2 = MakeVectorE0E1(b0, b1); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } - [Test, Pairwise, Description("SHA256H2 , , .4S")] - public void Sha256h2_V([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [Random(RndCnt / 2)] ulong Z0, [Random(RndCnt / 2)] ulong Z1, - [Random(RndCnt / 2)] ulong A0, [Random(RndCnt / 2)] ulong A1, - [Random(RndCnt / 2)] ulong B0, [Random(RndCnt / 2)] ulong B1) + [Test, Pairwise] + public void Sha256h_Sha256h2_Sha256su1_V([ValueSource("_Sha256h_Sha256h2_Sha256su1_V_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [Random(RndCnt / 2)] ulong z0, [Random(RndCnt / 2)] ulong z1, + [Random(RndCnt / 2)] ulong a0, [Random(RndCnt / 2)] ulong a1, + [Random(RndCnt / 2)] ulong b0, [Random(RndCnt / 2)] ulong b1) { - uint Opcode = 0x5E005000; // SHA256H2 Q0, Q0, V0.4S - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z0, Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 v0 = MakeVectorE0E1(z0, z1); + Vector128 v1 = MakeVectorE0E1(a0, a1); + Vector128 v2 = MakeVectorE0E1(b0, b1); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - CompareAgainstUnicorn(); - } - - [Test, Pairwise, Description("SHA256SU1 .4S, .4S, .4S")] - public void Sha256su1_V([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [Random(RndCnt / 2)] ulong Z0, [Random(RndCnt / 2)] ulong Z1, - [Random(RndCnt / 2)] ulong A0, [Random(RndCnt / 2)] ulong A1, - [Random(RndCnt / 2)] ulong B0, [Random(RndCnt / 2)] ulong B1) - { - uint Opcode = 0x5E006000; // SHA256SU1 V0.4S, V0.4S, V0.4S - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - - Vector128 V0 = MakeVectorE0E1(Z0, Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SHADD ., ., .")] - public void Shadd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Shadd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E200400; // SHADD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E200400; // SHADD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SHADD ., ., .")] - public void Shadd_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Shadd_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E200400; // SHADD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E200400; // SHADD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SHSUB ., ., .")] - public void Shsub_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Shsub_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E202400; // SHSUB V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E202400; // SHSUB V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SHSUB ., ., .")] - public void Shsub_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Shsub_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E202400; // SHSUB V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E202400; // SHSUB V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void S_Max_Min_P_V([ValueSource("_S_Max_Min_P_V_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [Values(0b00u, 0b01u, 0b10u)] uint size, // Q0: <8B, 4H, 2S> + [Values(0b0u, 0b1u)] uint q) // Q1: <16B, 8H, 4S> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((size & 3) << 22); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * q); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SMLAL{2} ., ., .")] - public void Smlal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Smlal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x0E208000; // SMLAL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E208000; // SMLAL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SMLAL{2} ., ., .")] - public void Smlal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Smlal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E208000; // SMLAL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E208000; // SMLAL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SMLSL{2} ., ., .")] - public void Smlsl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Smlsl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x0E20A000; // SMLSL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E20A000; // SMLSL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SMLSL{2} ., ., .")] - public void Smlsl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Smlsl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E20A000; // SMLSL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E20A000; // SMLSL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SQADD , , ")] - public void Sqadd_S_B_H_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // + public void Sqadd_S_B_H_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong b, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { - uint Opcode = 0x5E200C00; // SQADD B0, B0, B0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x5E200C00; // SQADD B0, B0, B0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQADD ., ., .")] - public void Sqadd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sqadd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E200C00; // SQADD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E200C00; // SQADD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQADD ., ., .")] - public void Sqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Sqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E200C00; // SQADD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E200C00; // SQADD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQDMULH , , ")] - public void Sqdmulh_S_H_S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong A, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong B, + public void Sqdmulh_S_H_S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong z, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong a, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong b, [Values(0b01u, 0b10u)] uint size) // { - uint Opcode = 0x5E20B400; // SQDMULH B0, B0, B0 (RESERVED) - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x5E20B400; // SQDMULH B0, B0, B0 (RESERVED) + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQDMULH ., ., .")] - public void Sqdmulh_V_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, + public void Sqdmulh_V_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong b, [Values(0b01u, 0b10u)] uint size) // <4H, 2S> { - uint Opcode = 0x0E20B400; // SQDMULH V0.8B, V0.8B, V0.8B (RESERVED) - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E20B400; // SQDMULH V0.8B, V0.8B, V0.8B (RESERVED) + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQDMULH ., ., .")] - public void Sqdmulh_V_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, + public void Sqdmulh_V_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong b, [Values(0b01u, 0b10u)] uint size) // <8H, 4S> { - uint Opcode = 0x4E20B400; // SQDMULH V0.16B, V0.16B, V0.16B (RESERVED) - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E20B400; // SQDMULH V0.16B, V0.16B, V0.16B (RESERVED) + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQRDMULH , , ")] - public void Sqrdmulh_S_H_S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong A, - [ValueSource("_1H1S_")] [Random(RndCnt)] ulong B, + public void Sqrdmulh_S_H_S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong z, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong a, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong b, [Values(0b01u, 0b10u)] uint size) // { - uint Opcode = 0x7E20B400; // SQRDMULH B0, B0, B0 (RESERVED) - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x7E20B400; // SQRDMULH B0, B0, B0 (RESERVED) + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQRDMULH ., ., .")] - public void Sqrdmulh_V_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, + public void Sqrdmulh_V_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong b, [Values(0b01u, 0b10u)] uint size) // <4H, 2S> { - uint Opcode = 0x2E20B400; // SQRDMULH V0.8B, V0.8B, V0.8B (RESERVED) - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E20B400; // SQRDMULH V0.8B, V0.8B, V0.8B (RESERVED) + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQRDMULH ., ., .")] - public void Sqrdmulh_V_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, + public void Sqrdmulh_V_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong b, [Values(0b01u, 0b10u)] uint size) // <8H, 4S> { - uint Opcode = 0x6E20B400; // SQRDMULH V0.16B, V0.16B, V0.16B (RESERVED) - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E20B400; // SQRDMULH V0.16B, V0.16B, V0.16B (RESERVED) + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQSUB , , ")] - public void Sqsub_S_B_H_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // + public void Sqsub_S_B_H_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong b, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { - uint Opcode = 0x5E202C00; // SQSUB B0, B0, B0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x5E202C00; // SQSUB B0, B0, B0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQSUB ., ., .")] - public void Sqsub_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sqsub_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E202C00; // SQSUB V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E202C00; // SQSUB V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SQSUB ., ., .")] - public void Sqsub_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Sqsub_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E202C00; // SQSUB V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E202C00; // SQSUB V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("SRHADD ., ., .")] - public void Srhadd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Srhadd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E201400; // SRHADD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E201400; // SRHADD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SRHADD ., ., .")] - public void Srhadd_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Srhadd_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E201400; // SRHADD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E201400; // SRHADD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SSUBL{2} ., ., .")] - public void Ssubl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Ssubl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x0E202000; // SSUBL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E202000; // SSUBL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SSUBL{2} ., ., .")] - public void Ssubl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Ssubl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E202000; // SSUBL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E202000; // SSUBL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SSUBW{2} ., ., .")] - public void Ssubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Ssubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { - uint Opcode = 0x0E203000; // SSUBW V0.8H, V0.8H, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E203000; // SSUBW V0.8H, V0.8H, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SSUBW{2} ., ., .")] - public void Ssubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Ssubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { - uint Opcode = 0x4E203000; // SSUBW2 V0.8H, V0.8H, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E203000; // SSUBW2 V0.8H, V0.8H, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB , , ")] - public void Sub_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + public void Sub_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1D_")] [Random(RndCnt)] ulong b) { - uint Opcode = 0x7EE08400; // SUB D0, D0, D0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + uint opcode = 0x7EE08400; // SUB D0, D0, D0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB ., ., .")] - public void Sub_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Sub_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E208400; // SUB V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E208400; // SUB V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUB ., ., .")] - public void Sub_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Sub_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E208400; // SUB V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E208400; // SUB V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBHN{2} ., ., .")] - public void Subhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + public void Subhn_V_8H8B_4S4H_2D2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x0E206000; // SUBHN V0.8B, V0.8H, V0.8H - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E206000; // SUBHN V0.8B, V0.8H, V0.8H + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SUBHN{2} ., ., .")] - public void Subhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + public void Subhn_V_8H16B_4S8H_2D4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x4E206000; // SUBHN2 V0.16B, V0.8H, V0.8H - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E206000; // SUBHN2 V0.16B, V0.8H, V0.8H + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("TRN1 ., ., .")] - public void Trn1_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Trn1_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E002800; // TRN1 V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E002800; // TRN1 V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("TRN1 ., ., .")] - public void Trn1_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Trn1_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E002800; // TRN1 V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E002800; // TRN1 V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("TRN2 ., ., .")] - public void Trn2_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Trn2_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E006800; // TRN2 V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E006800; // TRN2 V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("TRN2 ., ., .")] - public void Trn2_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Trn2_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E006800; // TRN2 V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E006800; // TRN2 V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UABA ., ., .")] - public void Uaba_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uaba_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E207C00; // UABA V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E207C00; // UABA V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UABA ., ., .")] - public void Uaba_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uaba_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x6E207C00; // UABA V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E207C00; // UABA V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UABAL{2} ., ., .")] - public void Uabal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uabal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x2E205000; // UABAL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E205000; // UABAL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UABAL{2} ., ., .")] - public void Uabal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uabal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E205000; // UABAL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E205000; // UABAL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UABD ., ., .")] - public void Uabd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uabd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E207400; // UABD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E207400; // UABD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UABD ., ., .")] - public void Uabd_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uabd_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x6E207400; // UABD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E207400; // UABD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UABDL{2} ., ., .")] - public void Uabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x2E207000; // UABDL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E207000; // UABDL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UABDL{2} ., ., .")] - public void Uabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E207000; // UABDL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E207000; // UABDL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UADDL{2} ., ., .")] - public void Uaddl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uaddl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x2E200000; // UADDL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E200000; // UADDL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UADDL{2} ., ., .")] - public void Uaddl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uaddl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E200000; // UADDL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E200000; // UADDL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UADDW{2} ., ., .")] - public void Uaddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uaddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { - uint Opcode = 0x2E201000; // UADDW V0.8H, V0.8H, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E201000; // UADDW V0.8H, V0.8H, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UADDW{2} ., ., .")] - public void Uaddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uaddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { - uint Opcode = 0x6E201000; // UADDW2 V0.8H, V0.8H, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E201000; // UADDW2 V0.8H, V0.8H, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UHADD ., ., .")] - public void Uhadd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uhadd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E200400; // UHADD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E200400; // UHADD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UHADD ., ., .")] - public void Uhadd_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uhadd_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x6E200400; // UHADD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E200400; // UHADD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UHSUB ., ., .")] - public void Uhsub_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uhsub_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E202400; // UHSUB V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E202400; // UHSUB V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UHSUB ., ., .")] - public void Uhsub_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uhsub_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x6E202400; // UHSUB V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E202400; // UHSUB V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void U_Max_Min_P_V([ValueSource("_U_Max_Min_P_V_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [Values(0b00u, 0b01u, 0b10u)] uint size, // Q0: <8B, 4H, 2S> + [Values(0b0u, 0b1u)] uint q) // Q1: <16B, 8H, 4S> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((size & 3) << 22); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * q); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UMLAL{2} ., ., .")] - public void Umlal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Umlal_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x2E208000; // UMLAL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E208000; // UMLAL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UMLAL{2} ., ., .")] - public void Umlal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Umlal_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E208000; // UMLAL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E208000; // UMLAL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UMLSL{2} ., ., .")] - public void Umlsl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Umlsl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x2E20A000; // UMLSL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E20A000; // UMLSL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UMLSL{2} ., ., .")] - public void Umlsl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Umlsl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E20A000; // UMLSL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E20A000; // UMLSL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UQADD , , ")] - public void Uqadd_S_B_H_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // + public void Uqadd_S_B_H_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong b, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { - uint Opcode = 0x7E200C00; // UQADD B0, B0, B0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x7E200C00; // UQADD B0, B0, B0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("UQADD ., ., .")] - public void Uqadd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uqadd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E200C00; // UQADD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E200C00; // UQADD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("UQADD ., ., .")] - public void Uqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Uqadd_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E200C00; // UQADD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E200C00; // UQADD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("UQSUB , , ")] - public void Uqsub_S_B_H_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // + public void Uqsub_S_B_H_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong b, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { - uint Opcode = 0x7E202C00; // UQSUB B0, B0, B0 - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x7E202C00; // UQSUB B0, B0, B0 + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("UQSUB ., ., .")] - public void Uqsub_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uqsub_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E202C00; // UQSUB V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E202C00; // UQSUB V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("UQSUB ., ., .")] - public void Uqsub_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Uqsub_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E202C00; // UQSUB V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E202C00; // UQSUB V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); - CompareAgainstUnicorn(FpsrMask: FPSR.QC); + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); } [Test, Pairwise, Description("URHADD ., ., .")] - public void Urhadd_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Urhadd_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E201400; // URHADD V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E201400; // URHADD V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("URHADD ., ., .")] - public void Urhadd_V_16B_8H_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Urhadd_V_16B_8H_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x6E201400; // URHADD V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E201400; // URHADD V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("USUBL{2} ., ., .")] - public void Usubl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Usubl_V_8B8H_4H4S_2S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x2E202000; // USUBL V0.8H, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E202000; // USUBL V0.8H, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("USUBL{2} ., ., .")] - public void Usubl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Usubl_V_16B8H_8H4S_4S2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E202000; // USUBL2 V0.8H, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E202000; // USUBL2 V0.8H, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE1(A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE1(a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("USUBW{2} ., ., .")] - public void Usubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Usubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> { - uint Opcode = 0x2E203000; // USUBW V0.8H, V0.8H, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x2E203000; // USUBW V0.8H, V0.8H, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("USUBW{2} ., ., .")] - public void Usubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Usubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> { - uint Opcode = 0x6E203000; // USUBW2 V0.8H, V0.8H, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x6E203000; // USUBW2 V0.8H, V0.8H, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE1(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE1(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UZP1 ., ., .")] - public void Uzp1_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uzp1_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E001800; // UZP1 V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E001800; // UZP1 V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UZP1 ., ., .")] - public void Uzp1_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Uzp1_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E001800; // UZP1 V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E001800; // UZP1 V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UZP2 ., ., .")] - public void Uzp2_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Uzp2_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E005800; // UZP2 V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E005800; // UZP2 V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("UZP2 ., ., .")] - public void Uzp2_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Uzp2_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E005800; // UZP2 V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E005800; // UZP2 V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ZIP1 ., ., .")] - public void Zip1_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Zip1_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E003800; // ZIP1 V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E003800; // ZIP1 V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ZIP1 ., ., .")] - public void Zip1_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Zip1_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E003800; // ZIP1 V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E003800; // ZIP1 V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ZIP2 ., ., .")] - public void Zip2_V_8B_4H_2S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + public void Zip2_V_8B_4H_2S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E007800; // ZIP2 V0.8B, V0.8B, V0.8B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x0E007800; // ZIP2 V0.8B, V0.8B, V0.8B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } [Test, Pairwise, Description("ZIP2 ., ., .")] - public void Zip2_V_16B_8H_4S_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, - [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + public void Zip2_V_16B_8H_4S_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong b, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E007800; // ZIP2 V0.16B, V0.16B, V0.16B - Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= ((size & 3) << 22); + uint opcode = 0x4E007800; // ZIP2 V0.16B, V0.16B, V0.16B + opcode |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= ((size & 3) << 22); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); - Vector128 V2 = MakeVectorE0E1(B, B); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcode, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs b/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs index 4d14ab4859..d97bd7b082 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs @@ -1,19 +1,17 @@ #define SimdRegElem -using ChocolArm64.State; - using NUnit.Framework; using System.Runtime.Intrinsics; namespace Ryujinx.Tests.Cpu { - [Category("SimdRegElem")] // Tested: second half of 2018. + [Category("SimdRegElem")] public sealed class CpuTestSimdRegElem : CpuTest { #if SimdRegElem -#region "ValueSource" +#region "ValueSource (Types)" private static ulong[] _2S_() { return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, @@ -27,114 +25,81 @@ namespace Ryujinx.Tests.Cpu } #endregion +#region "ValueSource (Opcodes)" + private static uint[] _Mla_Mls_Mul_Ve_4H_8H_() + { + return new uint[] + { + 0x2F400000u, // MLA V0.4H, V0.4H, V0.H[0] + 0x2F404000u, // MLS V0.4H, V0.4H, V0.H[0] + 0x0F408000u // MUL V0.4H, V0.4H, V0.H[0] + }; + } + + private static uint[] _Mla_Mls_Mul_Ve_2S_4S_() + { + return new uint[] + { + 0x2F800000u, // MLA V0.2S, V0.2S, V0.S[0] + 0x2F804000u, // MLS V0.2S, V0.2S, V0.S[0] + 0x0F808000u // MUL V0.2S, V0.2S, V0.S[0] + }; + } +#endregion + private const int RndCnt = 2; - [Test, Pairwise, Description("MLA ., ., .[]")] - public void Mla_Ve_4H_8H([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H_")] [Random(RndCnt)] ulong B, - [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint Index, - [Values(0b0u, 0b1u)] uint Q) // <4H, 8H> + [Test, Pairwise] + public void Mla_Mls_Mul_Ve_4H_8H([ValueSource("_Mla_Mls_Mul_Ve_4H_8H_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H_")] [Random(RndCnt)] ulong a, + [ValueSource("_4H_")] [Random(RndCnt)] ulong b, + [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { - uint H = (Index & 4) >> 2; - uint L = (Index & 2) >> 1; - uint M = (Index & 1) >> 0; + uint h = (index >> 2) & 1; + uint l = (index >> 1) & 1; + uint m = index & 1; - uint Opcode = 0x2F400000; // MLA V0.4H, V0.4H, V0.H[0] - Opcode |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (L << 21) | (M << 20) | (H << 11); - Opcode |= ((Q & 1) << 30); + opcodes |= ((rm & 15) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (l << 21) | (m << 20) | (h << 11); + opcodes |= ((q & 1) << 30); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); - Vector128 V2 = MakeVectorE0E1(B, B * H); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * h); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } - [Test, Pairwise, Description("MLA ., ., .[]")] - public void Mla_Ve_2S_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_2S_")] [Random(RndCnt)] ulong B, - [Values(0u, 1u, 2u, 3u)] uint Index, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + [Test, Pairwise] + public void Mla_Mls_Mul_Ve_2S_4S([ValueSource("_Mla_Mls_Mul_Ve_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_2S_")] [Random(RndCnt)] ulong b, + [Values(0u, 1u, 2u, 3u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { - uint H = (Index & 2) >> 1; - uint L = (Index & 1) >> 0; + uint h = (index >> 1) & 1; + uint l = index & 1; - uint Opcode = 0x2F800000; // MLA V0.2S, V0.2S, V0.S[0] - Opcode |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (L << 21) | (H << 11); - Opcode |= ((Q & 1) << 30); + opcodes |= ((rm & 15) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (l << 21) | (h << 11); + opcodes |= ((q & 1) << 30); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); - Vector128 V2 = MakeVectorE0E1(B, B * H); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * h); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - CompareAgainstUnicorn(); - } - - [Test, Pairwise, Description("MLS ., ., .[]")] - public void Mls_Ve_4H_8H([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H_")] [Random(RndCnt)] ulong B, - [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint Index, - [Values(0b0u, 0b1u)] uint Q) // <4H, 8H> - { - uint H = (Index & 4) >> 2; - uint L = (Index & 2) >> 1; - uint M = (Index & 1) >> 0; - - uint Opcode = 0x2F404000; // MLS V0.4H, V0.4H, V0.H[0] - Opcode |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (L << 21) | (M << 20) | (H << 11); - Opcode |= ((Q & 1) << 30); - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); - Vector128 V2 = MakeVectorE0E1(B, B * H); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - CompareAgainstUnicorn(); - } - - [Test, Pairwise, Description("MLS ., ., .[]")] - public void Mls_Ve_2S_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_2S_")] [Random(RndCnt)] ulong B, - [Values(0u, 1u, 2u, 3u)] uint Index, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> - { - uint H = (Index & 2) >> 1; - uint L = (Index & 1) >> 0; - - uint Opcode = 0x2F804000; // MLS V0.2S, V0.2S, V0.S[0] - Opcode |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (L << 21) | (H << 11); - Opcode |= ((Q & 1) << 30); - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); - Vector128 V2 = MakeVectorE0E1(B, B * H); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs b/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs new file mode 100644 index 0000000000..51027195bb --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs @@ -0,0 +1,446 @@ +#define SimdRegElemF + +using NUnit.Framework; + +using System.Collections.Generic; +using System.Runtime.Intrinsics; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdRegElemF")] + public sealed class CpuTestSimdRegElemF : CpuTest + { +#if SimdRegElemF + +#region "ValueSource (Types)" + private static IEnumerable _1S_F_() + { + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x0000000080800000ul; // -Min Normal + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0000000000800000ul; // +Min Normal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x0000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0x00000000FF800000ul; // -Infinity + yield return 0x000000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong grbg = TestContext.CurrentContext.Random.NextUInt(); + ulong rnd1 = GenNormalS(); + ulong rnd2 = GenSubnormalS(); + + yield return (grbg << 32) | rnd1; + yield return (grbg << 32) | rnd2; + } + } + + private static IEnumerable _2S_F_() + { + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x8080000080800000ul; // -Min Normal + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0080000000800000ul; // +Min Normal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFF800000FF800000ul; // -Infinity + yield return 0x7F8000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = GenNormalS(); + ulong rnd2 = GenSubnormalS(); + + yield return (rnd1 << 32) | rnd1; + yield return (rnd2 << 32) | rnd2; + } + } + + private static IEnumerable _1D_F_() + { + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = GenNormalD(); + ulong rnd2 = GenSubnormalD(); + + yield return rnd1; + yield return rnd2; + } + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _F_Mla_Mls_Se_S_() + { + return new uint[] + { + 0x5F821020u, // FMLA S0, S1, V2.S[0] + 0x5F825020u // FMLS S0, S1, V2.S[0] + }; + } + + private static uint[] _F_Mla_Mls_Se_D_() + { + return new uint[] + { + 0x5FC21020u, // FMLA D0, D1, V2.D[0] + 0x5FC25020u // FMLS D0, D1, V2.D[0] + }; + } + + private static uint[] _F_Mla_Mls_Ve_2S_4S_() + { + return new uint[] + { + 0x0F801000u, // FMLA V0.2S, V0.2S, V0.S[0] + 0x0F805000u // FMLS V0.2S, V0.2S, V0.S[0] + }; + } + + private static uint[] _F_Mla_Mls_Ve_2D_() + { + return new uint[] + { + 0x4FC01000u, // FMLA V0.2D, V0.2D, V0.D[0] + 0x4FC05000u // FMLS V0.2D, V0.2D, V0.D[0] + }; + } + + private static uint[] _F_Mul_Mulx_Se_S_() + { + return new uint[] + { + 0x5F829020u, // FMUL S0, S1, V2.S[0] + 0x7F829020u // FMULX S0, S1, V2.S[0] + }; + } + + private static uint[] _F_Mul_Mulx_Se_D_() + { + return new uint[] + { + 0x5FC29020u, // FMUL D0, D1, V2.D[0] + 0x7FC29020u // FMULX D0, D1, V2.D[0] + }; + } + + private static uint[] _F_Mul_Mulx_Ve_2S_4S_() + { + return new uint[] + { + 0x0F809000u, // FMUL V0.2S, V0.2S, V0.S[0] + 0x2F809000u // FMULX V0.2S, V0.2S, V0.S[0] + }; + } + + private static uint[] _F_Mul_Mulx_Ve_2D_() + { + return new uint[] + { + 0x4FC09000u, // FMUL V0.2D, V0.2D, V0.D[0] + 0x6FC09000u // FMULX V0.2D, V0.2D, V0.D[0] + }; + } +#endregion + + private const int RndCnt = 2; + + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_Se_S([ValueSource("_F_Mla_Mls_Se_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong z, + [ValueSource("_1S_F_")] ulong a, + [ValueSource("_2S_F_")] ulong b, + [Values(0u, 1u, 2u, 3u)] uint index) + { + uint h = (index >> 1) & 1; + uint l = index & 1; + + opcodes |= (l << 21) | (h << 11); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_Se_D([ValueSource("_F_Mla_Mls_Se_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b, + [Values(0u, 1u)] uint index) + { + uint h = index & 1; + + opcodes |= h << 11; + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_Ve_2S_4S([ValueSource("_F_Mla_Mls_Ve_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [ValueSource("_2S_F_")] ulong b, + [Values(0u, 1u, 2u, 3u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + uint h = (index >> 1) & 1; + uint l = index & 1; + + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (l << 21) | (h << 11); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsS); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_Ve_2D([ValueSource("_F_Mla_Mls_Ve_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b, + [Values(0u, 1u)] uint index) + { + uint h = index & 1; + + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= h << 11; + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); + } + + [Test, Pairwise] [Explicit] + public void F_Mul_Mulx_Se_S([ValueSource("_F_Mul_Mulx_Se_S_")] uint opcodes, + [ValueSource("_1S_F_")] ulong a, + [ValueSource("_2S_F_")] ulong b, + [Values(0u, 1u, 2u, 3u)] uint index) + { + uint h = (index >> 1) & 1; + uint l = index & 1; + + opcodes |= (l << 21) | (h << 11); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Mul_Mulx_Se_D([ValueSource("_F_Mul_Mulx_Se_D_")] uint opcodes, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b, + [Values(0u, 1u)] uint index) + { + uint h = index & 1; + + opcodes |= h << 11; + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Mul_Mulx_Ve_2S_4S([ValueSource("_F_Mul_Mulx_Ve_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_2S_F_")] ulong z, + [ValueSource("_2S_F_")] ulong a, + [ValueSource("_2S_F_")] ulong b, + [Values(0u, 1u, 2u, 3u)] uint index, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + uint h = (index >> 1) & 1; + uint l = index & 1; + + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (l << 21) | (h << 11); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } + + [Test, Pairwise] [Explicit] + public void F_Mul_Mulx_Ve_2D([ValueSource("_F_Mul_Mulx_Ve_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_1D_F_")] ulong z, + [ValueSource("_1D_F_")] ulong a, + [ValueSource("_1D_F_")] ulong b, + [Values(0u, 1u)] uint index) + { + uint h = index & 1; + + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= h << 11; + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b * h); + + int rnd = (int)TestContext.CurrentContext.Random.NextUInt(); + + int fpcr = rnd & (1 << (int)Fpcr.Fz); + fpcr |= rnd & (1 << (int)Fpcr.Dn); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, fpcr: fpcr); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Ioc | Fpsr.Idc); + } +#endif + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs b/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs index 7728522265..c9c4c1ed1e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs @@ -1,14 +1,12 @@ #define SimdShImm -using ChocolArm64.State; - using NUnit.Framework; using System.Runtime.Intrinsics; namespace Ryujinx.Tests.Cpu { - [Category("SimdShImm")] // Tested: second half of 2018. + [Category("SimdShImm")] public sealed class CpuTestSimdShImm : CpuTest { #if SimdShImm @@ -20,6 +18,18 @@ namespace Ryujinx.Tests.Cpu 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } + private static ulong[] _1H_() + { + return new ulong[] { 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul }; + } + + private static ulong[] _1S_() + { + return new ulong[] { 0x0000000000000000ul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul }; + } + private static ulong[] _2S_() { return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, @@ -114,231 +124,537 @@ namespace Ryujinx.Tests.Cpu 0x6F401400u // USRA V0.2D, V0.2D, #64 }; } + + private static uint[] _ShrImmNarrow_V_8H8B_8H16B_() + { + return new uint[] + { + 0x0F088C00u, // RSHRN V0.8B, V0.8H, #8 + 0x0F088400u // SHRN V0.8B, V0.8H, #8 + }; + } + + private static uint[] _ShrImmNarrow_V_4S4H_4S8H_() + { + return new uint[] + { + 0x0F108C00u, // RSHRN V0.4H, V0.4S, #16 + 0x0F108400u // SHRN V0.4H, V0.4S, #16 + }; + } + + private static uint[] _ShrImmNarrow_V_2D2S_2D4S_() + { + return new uint[] + { + 0x0F208C00u, // RSHRN V0.2S, V0.2D, #32 + 0x0F208400u // SHRN V0.2S, V0.2D, #32 + }; + } + + private static uint[] _ShrImmSaturatingNarrow_S_HB_() + { + return new uint[] + { + 0x5F089C00u, // SQRSHRN B0, H0, #8 + 0x7F089C00u, // UQRSHRN B0, H0, #8 + 0x7F088C00u, // SQRSHRUN B0, H0, #8 + 0x5F089400u, // SQSHRN B0, H0, #8 + 0x7F089400u, // UQSHRN B0, H0, #8 + 0x7F088400u // SQSHRUN B0, H0, #8 + }; + } + + private static uint[] _ShrImmSaturatingNarrow_S_SH_() + { + return new uint[] + { + 0x5F109C00u, // SQRSHRN H0, S0, #16 + 0x7F109C00u, // UQRSHRN H0, S0, #16 + 0x7F108C00u, // SQRSHRUN H0, S0, #16 + 0x5F109400u, // SQSHRN H0, S0, #16 + 0x7F109400u, // UQSHRN H0, S0, #16 + 0x7F108400u // SQSHRUN H0, S0, #16 + }; + } + + private static uint[] _ShrImmSaturatingNarrow_S_DS_() + { + return new uint[] + { + 0x5F209C00u, // SQRSHRN S0, D0, #32 + 0x7F209C00u, // UQRSHRN S0, D0, #32 + 0x7F208C00u, // SQRSHRUN S0, D0, #32 + 0x5F209400u, // SQSHRN S0, D0, #32 + 0x7F209400u, // UQSHRN S0, D0, #32 + 0x7F208400u // SQSHRUN S0, D0, #32 + }; + } + + private static uint[] _ShrImmSaturatingNarrow_V_8H8B_8H16B_() + { + return new uint[] + { + 0x0F089C00u, // SQRSHRN V0.8B, V0.8H, #8 + 0x2F089C00u, // UQRSHRN V0.8B, V0.8H, #8 + 0x2F088C00u, // SQRSHRUN V0.8B, V0.8H, #8 + 0x0F089400u, // SQSHRN V0.8B, V0.8H, #8 + 0x2F089400u, // UQSHRN V0.8B, V0.8H, #8 + 0x2F088400u // SQSHRUN V0.8B, V0.8H, #8 + }; + } + + private static uint[] _ShrImmSaturatingNarrow_V_4S4H_4S8H_() + { + return new uint[] + { + 0x0F109C00u, // SQRSHRN V0.4H, V0.4S, #16 + 0x2F109C00u, // UQRSHRN V0.4H, V0.4S, #16 + 0x2F108C00u, // SQRSHRUN V0.4H, V0.4S, #16 + 0x0F109400u, // SQSHRN V0.4H, V0.4S, #16 + 0x2F109400u, // UQSHRN V0.4H, V0.4S, #16 + 0x2F108400u // SQSHRUN V0.4H, V0.4S, #16 + }; + } + + private static uint[] _ShrImmSaturatingNarrow_V_2D2S_2D4S_() + { + return new uint[] + { + 0x0F209C00u, // SQRSHRN V0.2S, V0.2D, #32 + 0x2F209C00u, // UQRSHRN V0.2S, V0.2D, #32 + 0x2F208C00u, // SQRSHRUN V0.2S, V0.2D, #32 + 0x0F209400u, // SQSHRN V0.2S, V0.2D, #32 + 0x2F209400u, // UQSHRN V0.2S, V0.2D, #32 + 0x2F208400u // SQSHRUN V0.2S, V0.2D, #32 + }; + } #endregion private const int RndCnt = 2; [Test, Pairwise, Description("SHL , , #")] - public void Shl_S_D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [Range(0u, 63u)] uint Shift) + public void Shl_S_D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(0u, 63u)] uint shift) { - uint ImmHB = (64 + Shift) & 0x7F; + uint immHb = (64 + shift) & 0x7F; - uint Opcode = 0x5F405400; // SHL D0, D0, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (ImmHB << 16); + uint opcode = 0x5F405400; // SHL D0, D0, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (immHb << 16); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SHL ., ., #")] - public void Shl_V_8B_16B([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [Range(0u, 7u)] uint Shift, - [Values(0b0u, 0b1u)] uint Q) // <8B, 16B> + public void Shl_V_8B_16B([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [Range(0u, 7u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { - uint ImmHB = (8 + Shift) & 0x7F; + uint immHb = (8 + shift) & 0x7F; - uint Opcode = 0x0F085400; // SHL V0.8B, V0.8B, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (ImmHB << 16); - Opcode |= ((Q & 1) << 30); + uint opcode = 0x0F085400; // SHL V0.8B, V0.8B, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (immHb << 16); + opcode |= ((q & 1) << 30); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SHL ., ., #")] - public void Shl_V_4H_8H([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong A, - [Range(0u, 15u)] uint Shift, - [Values(0b0u, 0b1u)] uint Q) // <4H, 8H> + public void Shl_V_4H_8H([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H_")] [Random(RndCnt)] ulong a, + [Range(0u, 15u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { - uint ImmHB = (16 + Shift) & 0x7F; + uint immHb = (16 + shift) & 0x7F; - uint Opcode = 0x0F105400; // SHL V0.4H, V0.4H, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (ImmHB << 16); - Opcode |= ((Q & 1) << 30); + uint opcode = 0x0F105400; // SHL V0.4H, V0.4H, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (immHb << 16); + opcode |= ((q & 1) << 30); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SHL ., ., #")] - public void Shl_V_2S_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong A, - [Range(0u, 31u)] uint Shift, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + public void Shl_V_2S_4S([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [Range(0u, 31u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { - uint ImmHB = (32 + Shift) & 0x7F; + uint immHb = (32 + shift) & 0x7F; - uint Opcode = 0x0F205400; // SHL V0.2S, V0.2S, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (ImmHB << 16); - Opcode |= ((Q & 1) << 30); + uint opcode = 0x0F205400; // SHL V0.2S, V0.2S, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (immHb << 16); + opcode |= ((q & 1) << 30); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise, Description("SHL ., ., #")] - public void Shl_V_2D([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [Range(0u, 63u)] uint Shift) + public void Shl_V_2D([Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(0u, 63u)] uint shift) { - uint ImmHB = (64 + Shift) & 0x7F; + uint immHb = (64 + shift) & 0x7F; - uint Opcode = 0x4F405400; // SHL V0.2D, V0.2D, #0 - Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (ImmHB << 16); + uint opcode = 0x4F405400; // SHL V0.2D, V0.2D, #0 + opcode |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcode |= (immHb << 16); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + SingleOpcode(opcode, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise] - public void ShrImm_S_D([ValueSource("_ShrImm_S_D_")] uint Opcodes, - [Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [Range(1u, 64u)] uint Shift) + public void ShrImm_S_D([ValueSource("_ShrImm_S_D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(1u, 64u)] uint shift) { - uint ImmHB = (128 - Shift) & 0x7F; + uint immHb = (128 - shift) & 0x7F; - Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcodes |= (ImmHB << 16); + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0(A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1); + SingleOpcode(opcodes, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise] - public void ShrImm_V_8B_16B([ValueSource("_ShrImm_V_8B_16B_")] uint Opcodes, - [Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, - [ValueSource("_8B_")] [Random(RndCnt)] ulong A, - [Range(1u, 8u)] uint Shift, - [Values(0b0u, 0b1u)] uint Q) // <8B, 16B> + public void ShrImm_V_8B_16B([ValueSource("_ShrImm_V_8B_16B_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_8B_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B_")] [Random(RndCnt)] ulong a, + [Range(1u, 8u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { - uint ImmHB = (16 - Shift) & 0x7F; + uint immHb = (16 - shift) & 0x7F; - Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcodes |= (ImmHB << 16); - Opcodes |= ((Q & 1) << 30); + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1); + SingleOpcode(opcodes, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise] - public void ShrImm_V_4H_8H([ValueSource("_ShrImm_V_4H_8H_")] uint Opcodes, - [Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong A, - [Range(1u, 16u)] uint Shift, - [Values(0b0u, 0b1u)] uint Q) // <4H, 8H> + public void ShrImm_V_4H_8H([ValueSource("_ShrImm_V_4H_8H_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H_")] [Random(RndCnt)] ulong a, + [Range(1u, 16u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { - uint ImmHB = (32 - Shift) & 0x7F; + uint immHb = (32 - shift) & 0x7F; - Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcodes |= (ImmHB << 16); - Opcodes |= ((Q & 1) << 30); + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1); + SingleOpcode(opcodes, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise] - public void ShrImm_V_2S_4S([ValueSource("_ShrImm_V_2S_4S_")] uint Opcodes, - [Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong A, - [Range(1u, 32u)] uint Shift, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + public void ShrImm_V_2S_4S([ValueSource("_ShrImm_V_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [Range(1u, 32u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { - uint ImmHB = (64 - Shift) & 0x7F; + uint immHb = (64 - shift) & 0x7F; - Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcodes |= (ImmHB << 16); - Opcodes |= ((Q & 1) << 30); + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1); + SingleOpcode(opcodes, v0: v0, v1: v1); CompareAgainstUnicorn(); } [Test, Pairwise] - public void ShrImm_V_2D([ValueSource("_ShrImm_V_2D_")] uint Opcodes, - [Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, - [ValueSource("_1D_")] [Random(RndCnt)] ulong A, - [Range(1u, 64u)] uint Shift) + public void ShrImm_V_2D([ValueSource("_ShrImm_V_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(1u, 64u)] uint shift) { - uint ImmHB = (128 - Shift) & 0x7F; + uint immHb = (128 - shift) & 0x7F; - Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcodes |= (ImmHB << 16); + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1); + SingleOpcode(opcodes, v0: v0, v1: v1); CompareAgainstUnicorn(); } + + [Test, Pairwise] + public void ShrImmNarrow_V_8H8B_8H16B([ValueSource("_ShrImmNarrow_V_8H8B_8H16B_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H_")] [Random(RndCnt)] ulong a, + [Range(1u, 8u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <8H8B, 8H16B> + { + uint immHb = (16 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void ShrImmNarrow_V_4S4H_4S8H([ValueSource("_ShrImmNarrow_V_4S4H_4S8H_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [Range(1u, 16u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <4S4H, 4S8H> + { + uint immHb = (32 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void ShrImmNarrow_V_2D2S_2D4S([ValueSource("_ShrImmNarrow_V_2D2S_2D4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(1u, 32u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <2D2S, 2D4S> + { + uint immHb = (64 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void ShrImmSaturatingNarrow_S_HB([ValueSource("_ShrImmSaturatingNarrow_S_HB_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1H_")] [Random(RndCnt)] ulong z, + [ValueSource("_1H_")] [Random(RndCnt)] ulong a, + [Range(1u, 8u)] uint shift) + { + uint immHb = (16 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); + } + + [Test, Pairwise] + public void ShrImmSaturatingNarrow_S_SH([ValueSource("_ShrImmSaturatingNarrow_S_SH_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1S_")] [Random(RndCnt)] ulong z, + [ValueSource("_1S_")] [Random(RndCnt)] ulong a, + [Range(1u, 16u)] uint shift) + { + uint immHb = (32 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); + } + + [Test, Pairwise] + public void ShrImmSaturatingNarrow_S_DS([ValueSource("_ShrImmSaturatingNarrow_S_DS_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(1u, 32u)] uint shift) + { + uint immHb = (64 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); + } + + [Test, Pairwise] + public void ShrImmSaturatingNarrow_V_8H8B_8H16B([ValueSource("_ShrImmSaturatingNarrow_V_8H8B_8H16B_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_4H_")] [Random(RndCnt)] ulong z, + [ValueSource("_4H_")] [Random(RndCnt)] ulong a, + [Range(1u, 8u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <8H8B, 8H16B> + { + uint immHb = (16 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); + } + + [Test, Pairwise] + public void ShrImmSaturatingNarrow_V_4S4H_4S8H([ValueSource("_ShrImmSaturatingNarrow_V_4S4H_4S8H_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [Range(1u, 16u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <4S4H, 4S8H> + { + uint immHb = (32 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); + } + + [Test, Pairwise] + public void ShrImmSaturatingNarrow_V_2D2S_2D4S([ValueSource("_ShrImmSaturatingNarrow_V_2D2S_2D4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Range(1u, 32u)] uint shift, + [Values(0b0u, 0b1u)] uint q) // <2D2S, 2D4S> + { + uint immHb = (64 - shift) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(fpsrMask: Fpsr.Qc); + } #endif } } diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index 27c2bf7866..35405c769f 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/Ryujinx.sln b/Ryujinx.sln index cd04dabc20..148224faa4 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx\Ryujinx. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "Ryujinx.HLE\Ryujinx.HLE.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChocolArm64", "ChocolArm64\ChocolArm64.csproj", "{2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}" @@ -15,9 +17,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics", "Ryujinx EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luea", "Ryujinx.LLE\Luea.csproj", "{8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luea", "Ryujinx.LLE\Luea.csproj", "{8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,6 +37,10 @@ Global {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.Build.0 = Release|Any CPU + {D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}.Release|Any CPU.Build.0 = Release|Any CPU {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.Build.0 = Debug|Any CPU {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -57,6 +65,10 @@ Global {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Release|Any CPU.Build.0 = Release|Any CPU + {5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index 748d1dbf87..dcead64839 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -1,5 +1,6 @@ +using LibHac; +using Ryujinx.Common.Logging; using Ryujinx.HLE; -using Ryujinx.HLE.Logging; using Ryujinx.UI.Input; using System; using System.Globalization; @@ -15,148 +16,157 @@ namespace Ryujinx public static JoyConKeyboard JoyConKeyboard { get; private set; } public static JoyConController JoyConController { get; private set; } - public static void Read(Switch Device) + public static void Read(Switch device) { - string IniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + string iniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - string IniPath = Path.Combine(IniFolder, "Ryujinx.conf"); + string iniPath = Path.Combine(iniFolder, "Ryujinx.conf"); - IniParser Parser = new IniParser(IniPath); + IniParser parser = new IniParser(iniPath); - GraphicsConfig.ShadersDumpPath = Parser.Value("Graphics_Shaders_Dump_Path"); + GraphicsConfig.ShadersDumpPath = parser.Value("Graphics_Shaders_Dump_Path"); - Device.Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"))); - Device.Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"))); - Device.Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info"))); - Device.Log.SetEnable(LogLevel.Warning, Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"))); - Device.Log.SetEnable(LogLevel.Error, Convert.ToBoolean(Parser.Value("Logging_Enable_Error"))); + Logger.SetEnable(LogLevel.Debug, Convert.ToBoolean(parser.Value("Logging_Enable_Debug"))); + Logger.SetEnable(LogLevel.Stub, Convert.ToBoolean(parser.Value("Logging_Enable_Stub"))); + Logger.SetEnable(LogLevel.Info, Convert.ToBoolean(parser.Value("Logging_Enable_Info"))); + Logger.SetEnable(LogLevel.Warning, Convert.ToBoolean(parser.Value("Logging_Enable_Warn"))); + Logger.SetEnable(LogLevel.Error, Convert.ToBoolean(parser.Value("Logging_Enable_Error"))); - Device.System.State.DockedMode = Convert.ToBoolean(Parser.Value("Docked_Mode")); - - Device.EnableDeviceVsync = Convert.ToBoolean(Parser.Value("Enable_Vsync")); - - string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries); + string[] filteredLogClasses = parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries); //When the classes are specified on the list, we only //enable the classes that are on the list. //So, first disable everything, then enable //the classes that the user added to the list. - if (FilteredLogClasses.Length > 0) + if (filteredLogClasses.Length > 0) { foreach (LogClass Class in Enum.GetValues(typeof(LogClass))) { - Device.Log.SetEnable(Class, false); + Logger.SetEnable(Class, false); } } - foreach (string LogClass in FilteredLogClasses) + foreach (string logClass in filteredLogClasses) { - if (!string.IsNullOrEmpty(LogClass.Trim())) + if (!string.IsNullOrEmpty(logClass.Trim())) { foreach (LogClass Class in Enum.GetValues(typeof(LogClass))) { - if (Class.ToString().ToLower().Contains(LogClass.Trim().ToLower())) + if (Class.ToString().ToLower().Contains(logClass.Trim().ToLower())) { - Device.Log.SetEnable(Class, true); + Logger.SetEnable(Class, true); } } } } + device.System.State.DockedMode = Convert.ToBoolean(parser.Value("Docked_Mode")); + + device.EnableDeviceVsync = Convert.ToBoolean(parser.Value("Enable_Vsync")); + + if (Convert.ToBoolean(parser.Value("Enable_MultiCore_Scheduling"))) + { + device.System.EnableMultiCoreScheduling(); + } + + device.System.FsIntegrityCheckLevel = Convert.ToBoolean(parser.Value("Enable_FS_Integrity_Checks")) + ? IntegrityCheckLevel.ErrorOnInvalid + : IntegrityCheckLevel.None; + JoyConKeyboard = new JoyConKeyboard( new JoyConKeyboardLeft { - StickUp = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Stick_Up")), - StickDown = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Stick_Down")), - StickLeft = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Stick_Left")), - StickRight = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Stick_Right")), - StickButton = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Stick_Button")), - DPadUp = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_DPad_Up")), - DPadDown = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_DPad_Down")), - DPadLeft = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_DPad_Left")), - DPadRight = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_DPad_Right")), - ButtonMinus = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Button_Minus")), - ButtonL = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Button_L")), - ButtonZL = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Button_ZL")) + StickUp = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Up")), + StickDown = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Down")), + StickLeft = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Left")), + StickRight = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Right")), + StickButton = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Stick_Button")), + DPadUp = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Up")), + DPadDown = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Down")), + DPadLeft = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Left")), + DPadRight = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_DPad_Right")), + ButtonMinus = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_Minus")), + ButtonL = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_L")), + ButtonZl = Convert.ToInt16(parser.Value("Controls_Left_JoyConKeyboard_Button_ZL")) }, new JoyConKeyboardRight { - StickUp = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Stick_Up")), - StickDown = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Stick_Down")), - StickLeft = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Stick_Left")), - StickRight = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Stick_Right")), - StickButton = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Stick_Button")), - ButtonA = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_A")), - ButtonB = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_B")), - ButtonX = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_X")), - ButtonY = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_Y")), - ButtonPlus = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_Plus")), - ButtonR = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_R")), - ButtonZR = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_ZR")) + StickUp = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Up")), + StickDown = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Down")), + StickLeft = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Left")), + StickRight = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Right")), + StickButton = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Stick_Button")), + ButtonA = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_A")), + ButtonB = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_B")), + ButtonX = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_X")), + ButtonY = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_Y")), + ButtonPlus = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_Plus")), + ButtonR = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_R")), + ButtonZr = Convert.ToInt16(parser.Value("Controls_Right_JoyConKeyboard_Button_ZR")) }); JoyConController = new JoyConController( - Convert.ToBoolean(Parser.Value("GamePad_Enable")), - Convert.ToInt32 (Parser.Value("GamePad_Index")), - (float)Convert.ToDouble (Parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture), - (float)Convert.ToDouble (Parser.Value("GamePad_Trigger_Threshold"), CultureInfo.InvariantCulture), + Convert.ToBoolean(parser.Value("GamePad_Enable")), + Convert.ToInt32 (parser.Value("GamePad_Index")), + (float)Convert.ToDouble (parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture), + (float)Convert.ToDouble (parser.Value("GamePad_Trigger_Threshold"), CultureInfo.InvariantCulture), new JoyConControllerLeft { - Stick = ToID(Parser.Value("Controls_Left_JoyConController_Stick")), - StickButton = ToID(Parser.Value("Controls_Left_JoyConController_Stick_Button")), - DPadUp = ToID(Parser.Value("Controls_Left_JoyConController_DPad_Up")), - DPadDown = ToID(Parser.Value("Controls_Left_JoyConController_DPad_Down")), - DPadLeft = ToID(Parser.Value("Controls_Left_JoyConController_DPad_Left")), - DPadRight = ToID(Parser.Value("Controls_Left_JoyConController_DPad_Right")), - ButtonMinus = ToID(Parser.Value("Controls_Left_JoyConController_Button_Minus")), - ButtonL = ToID(Parser.Value("Controls_Left_JoyConController_Button_L")), - ButtonZL = ToID(Parser.Value("Controls_Left_JoyConController_Button_ZL")) + Stick = ToId(parser.Value("Controls_Left_JoyConController_Stick")), + StickButton = ToId(parser.Value("Controls_Left_JoyConController_Stick_Button")), + DPadUp = ToId(parser.Value("Controls_Left_JoyConController_DPad_Up")), + DPadDown = ToId(parser.Value("Controls_Left_JoyConController_DPad_Down")), + DPadLeft = ToId(parser.Value("Controls_Left_JoyConController_DPad_Left")), + DPadRight = ToId(parser.Value("Controls_Left_JoyConController_DPad_Right")), + ButtonMinus = ToId(parser.Value("Controls_Left_JoyConController_Button_Minus")), + ButtonL = ToId(parser.Value("Controls_Left_JoyConController_Button_L")), + ButtonZl = ToId(parser.Value("Controls_Left_JoyConController_Button_ZL")) }, new JoyConControllerRight { - Stick = ToID(Parser.Value("Controls_Right_JoyConController_Stick")), - StickButton = ToID(Parser.Value("Controls_Right_JoyConController_Stick_Button")), - ButtonA = ToID(Parser.Value("Controls_Right_JoyConController_Button_A")), - ButtonB = ToID(Parser.Value("Controls_Right_JoyConController_Button_B")), - ButtonX = ToID(Parser.Value("Controls_Right_JoyConController_Button_X")), - ButtonY = ToID(Parser.Value("Controls_Right_JoyConController_Button_Y")), - ButtonPlus = ToID(Parser.Value("Controls_Right_JoyConController_Button_Plus")), - ButtonR = ToID(Parser.Value("Controls_Right_JoyConController_Button_R")), - ButtonZR = ToID(Parser.Value("Controls_Right_JoyConController_Button_ZR")) + Stick = ToId(parser.Value("Controls_Right_JoyConController_Stick")), + StickButton = ToId(parser.Value("Controls_Right_JoyConController_Stick_Button")), + ButtonA = ToId(parser.Value("Controls_Right_JoyConController_Button_A")), + ButtonB = ToId(parser.Value("Controls_Right_JoyConController_Button_B")), + ButtonX = ToId(parser.Value("Controls_Right_JoyConController_Button_X")), + ButtonY = ToId(parser.Value("Controls_Right_JoyConController_Button_Y")), + ButtonPlus = ToId(parser.Value("Controls_Right_JoyConController_Button_Plus")), + ButtonR = ToId(parser.Value("Controls_Right_JoyConController_Button_R")), + ButtonZr = ToId(parser.Value("Controls_Right_JoyConController_Button_ZR")) }); } - private static ControllerInputID ToID(string Key) + private static ControllerInputId ToId(string key) { - switch (Key.ToUpper()) + switch (key.ToUpper()) { - case "LSTICK": return ControllerInputID.LStick; - case "DPADUP": return ControllerInputID.DPadUp; - case "DPADDOWN": return ControllerInputID.DPadDown; - case "DPADLEFT": return ControllerInputID.DPadLeft; - case "DPADRIGHT": return ControllerInputID.DPadRight; - case "BACK": return ControllerInputID.Back; - case "LSHOULDER": return ControllerInputID.LShoulder; - case "LTRIGGER": return ControllerInputID.LTrigger; + case "LSTICK": return ControllerInputId.LStick; + case "DPADUP": return ControllerInputId.DPadUp; + case "DPADDOWN": return ControllerInputId.DPadDown; + case "DPADLEFT": return ControllerInputId.DPadLeft; + case "DPADRIGHT": return ControllerInputId.DPadRight; + case "BACK": return ControllerInputId.Back; + case "LSHOULDER": return ControllerInputId.LShoulder; + case "LTRIGGER": return ControllerInputId.LTrigger; - case "RSTICK": return ControllerInputID.RStick; - case "A": return ControllerInputID.A; - case "B": return ControllerInputID.B; - case "X": return ControllerInputID.X; - case "Y": return ControllerInputID.Y; - case "START": return ControllerInputID.Start; - case "RSHOULDER": return ControllerInputID.RShoulder; - case "RTRIGGER": return ControllerInputID.RTrigger; + case "RSTICK": return ControllerInputId.RStick; + case "A": return ControllerInputId.A; + case "B": return ControllerInputId.B; + case "X": return ControllerInputId.X; + case "Y": return ControllerInputId.Y; + case "START": return ControllerInputId.Start; + case "RSHOULDER": return ControllerInputId.RShoulder; + case "RTRIGGER": return ControllerInputId.RTrigger; - case "LJOYSTICK": return ControllerInputID.LJoystick; - case "RJOYSTICK": return ControllerInputID.RJoystick; + case "LJOYSTICK": return ControllerInputId.LJoystick; + case "RJOYSTICK": return ControllerInputId.RJoystick; - default: return ControllerInputID.Invalid; + default: return ControllerInputId.Invalid; } } } @@ -164,19 +174,19 @@ namespace Ryujinx //https://stackoverflow.com/a/37772571 public class IniParser { - private readonly Dictionary Values; + private readonly Dictionary _values; - public IniParser(string Path) + public IniParser(string path) { - Values = File.ReadLines(Path) - .Where(Line => !string.IsNullOrWhiteSpace(Line) && !Line.StartsWith('#')) - .Select(Line => Line.Split('=', 2)) - .ToDictionary(Parts => Parts[0].Trim(), Parts => Parts.Length > 1 ? Parts[1].Trim() : null); + _values = File.ReadLines(path) + .Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith('#')) + .Select(line => line.Split('=', 2)) + .ToDictionary(parts => parts[0].Trim(), parts => parts.Length > 1 ? parts[1].Trim() : null); } - public string Value(string Name) + public string Value(string name) { - return Values.TryGetValue(Name, out string Value) ? Value : null; + return _values.TryGetValue(name, out string value) ? value : null; } } } \ No newline at end of file diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Program.cs similarity index 51% rename from Ryujinx/Ui/Program.cs rename to Ryujinx/Program.cs index 053cf1be4b..f1d0a2ff9d 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Program.cs @@ -1,5 +1,5 @@ using Ryujinx.Audio; -using Ryujinx.Audio.OpenAL; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal.OpenGL; using Ryujinx.HLE; @@ -14,38 +14,38 @@ namespace Ryujinx { Console.Title = "Ryujinx Console"; - IGalRenderer Renderer = new OGLRenderer(); + IGalRenderer renderer = new OGLRenderer(); - IAalOutput AudioOut = new OpenALAudioOut(); + IAalOutput audioOut = InitializeAudioEngine(); - Switch Device = new Switch(Renderer, AudioOut); + Switch device = new Switch(renderer, audioOut); - Config.Read(Device); + Config.Read(device); - Device.Log.Updated += ConsoleLog.Log; + Logger.Updated += ConsoleLog.Log; if (args.Length == 1) { if (Directory.Exists(args[0])) { - string[] RomFsFiles = Directory.GetFiles(args[0], "*.istorage"); + string[] romFsFiles = Directory.GetFiles(args[0], "*.istorage"); - if (RomFsFiles.Length == 0) + if (romFsFiles.Length == 0) { - RomFsFiles = Directory.GetFiles(args[0], "*.romfs"); + romFsFiles = Directory.GetFiles(args[0], "*.romfs"); } - if (RomFsFiles.Length > 0) + if (romFsFiles.Length > 0) { Console.WriteLine("Loading as cart with RomFS."); - Device.LoadCart(args[0], RomFsFiles[0]); + device.LoadCart(args[0], romFsFiles[0]); } else { Console.WriteLine("Loading as cart WITHOUT RomFS."); - Device.LoadCart(args[0]); + device.LoadCart(args[0]); } } else if (File.Exists(args[0])) @@ -54,19 +54,19 @@ namespace Ryujinx { case ".xci": Console.WriteLine("Loading as XCI."); - Device.LoadXci(args[0]); + device.LoadXci(args[0]); break; case ".nca": Console.WriteLine("Loading as NCA."); - Device.LoadNca(args[0]); + device.LoadNca(args[0]); break; case ".nsp": Console.WriteLine("Loading as NSP."); - Device.LoadNsp(args[0]); + device.LoadNsp(args[0]); break; default: Console.WriteLine("Loading as homebrew."); - Device.LoadProgram(args[0]); + device.LoadProgram(args[0]); break; } } @@ -76,14 +76,34 @@ namespace Ryujinx Console.WriteLine("Please specify the folder with the NSOs/IStorage or a NSO/NRO."); } - using (GLScreen Screen = new GLScreen(Device, Renderer)) + using (GlScreen screen = new GlScreen(device, renderer)) { - Screen.MainLoop(); + screen.MainLoop(); - Device.Dispose(); + device.Dispose(); } - AudioOut.Dispose(); + audioOut.Dispose(); + } + + /// + /// Picks an audio output renderer supported on this machine + /// + /// An supported by this machine + private static IAalOutput InitializeAudioEngine() + { + if (SoundIoAudioOut.IsSupported) + { + return new SoundIoAudioOut(); + } + else if (OpenALAudioOut.IsSupported) + { + return new OpenALAudioOut(); + } + else + { + return new DummyAudioOut(); + } } } } diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index c497c08112..45f545d00e 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -28,6 +28,12 @@ Docked_Mode = false #Enable Game Vsync Enable_Vsync = true +#Enable or Disable Multi-core scheduling of threads +Enable_MultiCore_Scheduling = false + +#Enable integrity checks on Switch content files +Enable_FS_Integrity_Checks = true + #Controller Device Index GamePad_Index = 0 diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index ad132ada4a..1789ef2e9f 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 @@ -9,12 +9,12 @@ - + diff --git a/Ryujinx/Ui/ConsoleLog.cs b/Ryujinx/Ui/ConsoleLog.cs index 6fb92e333f..1ecd4cde44 100644 --- a/Ryujinx/Ui/ConsoleLog.cs +++ b/Ryujinx/Ui/ConsoleLog.cs @@ -1,4 +1,4 @@ -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -8,17 +8,17 @@ namespace Ryujinx { static class ConsoleLog { - private static Thread MessageThread; + private static Thread _messageThread; - private static BlockingCollection MessageQueue; + private static BlockingCollection _messageQueue; - private static Dictionary LogColors; + private static Dictionary _logColors; - private static object ConsoleLock; + private static object _consoleLock; static ConsoleLog() { - LogColors = new Dictionary() + _logColors = new Dictionary() { { LogLevel.Stub, ConsoleColor.DarkGray }, { LogLevel.Info, ConsoleColor.White }, @@ -26,17 +26,17 @@ namespace Ryujinx { LogLevel.Error, ConsoleColor.Red } }; - MessageQueue = new BlockingCollection(); + _messageQueue = new BlockingCollection(); - ConsoleLock = new object(); + _consoleLock = new object(); - MessageThread = new Thread(() => + _messageThread = new Thread(() => { - while (!MessageQueue.IsCompleted) + while (!_messageQueue.IsCompleted) { try { - PrintLog(MessageQueue.Take()); + PrintLog(_messageQueue.Take()); } catch (InvalidOperationException) { @@ -49,39 +49,39 @@ namespace Ryujinx } }); - MessageThread.IsBackground = true; - MessageThread.Start(); + _messageThread.IsBackground = true; + _messageThread.Start(); } private static void PrintLog(LogEventArgs e) { - string FormattedTime = e.Time.ToString(@"hh\:mm\:ss\.fff"); + string formattedTime = e.Time.ToString(@"hh\:mm\:ss\.fff"); - string CurrentThread = Thread.CurrentThread.ManagedThreadId.ToString("d4"); + string currentThread = Thread.CurrentThread.ManagedThreadId.ToString("d4"); - string Message = FormattedTime + " | " + CurrentThread + " " + e.Message; + string message = formattedTime + " | " + currentThread + " " + e.Message; - if (LogColors.TryGetValue(e.Level, out ConsoleColor Color)) + if (_logColors.TryGetValue(e.Level, out ConsoleColor color)) { - lock (ConsoleLock) + lock (_consoleLock) { - Console.ForegroundColor = Color; + Console.ForegroundColor = color; - Console.WriteLine(Message); + Console.WriteLine(message); Console.ResetColor(); } } else { - Console.WriteLine(Message); + Console.WriteLine(message); } } public static void Log(object sender, LogEventArgs e) { - if (!MessageQueue.IsAddingCompleted) + if (!_messageQueue.IsAddingCompleted) { - MessageQueue.Add(e); + _messageQueue.Add(e); } } } diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 8adff9c00d..a654d15033 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -11,37 +11,37 @@ using Stopwatch = System.Diagnostics.Stopwatch; namespace Ryujinx { - public class GLScreen : GameWindow + public class GlScreen : GameWindow { private const int TouchScreenWidth = 1280; private const int TouchScreenHeight = 720; - private const int TargetFPS = 60; + private const int TargetFps = 60; - private Switch Device; + private Switch _device; - private IGalRenderer Renderer; + private IGalRenderer _renderer; - private KeyboardState? Keyboard = null; + private KeyboardState? _keyboard = null; - private MouseState? Mouse = null; + private MouseState? _mouse = null; - private Thread RenderThread; + private Thread _renderThread; - private bool ResizeEvent; + private bool _resizeEvent; - private bool TitleEvent; + private bool _titleEvent; - private string NewTitle; + private string _newTitle; - public GLScreen(Switch Device, IGalRenderer Renderer) + public GlScreen(Switch device, IGalRenderer renderer) : base(1280, 720, new GraphicsMode(), "Ryujinx", 0, DisplayDevice.Default, 3, 3, GraphicsContextFlags.ForwardCompatible) { - this.Device = Device; - this.Renderer = Renderer; + _device = device; + _renderer = renderer; Location = new Point( (DisplayDevice.Default.Width / 2) - (Width / 2), @@ -52,40 +52,40 @@ namespace Ryujinx { MakeCurrent(); - Stopwatch Chrono = new Stopwatch(); + Stopwatch chrono = new Stopwatch(); - Chrono.Start(); + chrono.Start(); - long TicksPerFrame = Stopwatch.Frequency / TargetFPS; + long ticksPerFrame = Stopwatch.Frequency / TargetFps; - long Ticks = 0; + long ticks = 0; while (Exists && !IsExiting) { - if (Device.WaitFifo()) + if (_device.WaitFifo()) { - Device.ProcessFrame(); + _device.ProcessFrame(); } - Renderer.RunActions(); + _renderer.RunActions(); - if (ResizeEvent) + if (_resizeEvent) { - ResizeEvent = false; + _resizeEvent = false; - Renderer.RenderTarget.SetWindowSize(Width, Height); + _renderer.RenderTarget.SetWindowSize(Width, Height); } - Ticks += Chrono.ElapsedTicks; + ticks += chrono.ElapsedTicks; - Chrono.Restart(); + chrono.Restart(); - if (Ticks >= TicksPerFrame) + if (ticks >= ticksPerFrame) { RenderFrame(); //Queue max. 1 vsync - Ticks = Math.Min(Ticks - TicksPerFrame, TicksPerFrame); + ticks = Math.Min(ticks - ticksPerFrame, ticksPerFrame); } } } @@ -96,14 +96,14 @@ namespace Ryujinx Visible = true; - Renderer.RenderTarget.SetWindowSize(Width, Height); + _renderer.RenderTarget.SetWindowSize(Width, Height); Context.MakeCurrent(null); //OpenTK doesn't like sleeps in its thread, to avoid this a renderer thread is created - RenderThread = new Thread(RenderLoop); + _renderThread = new Thread(RenderLoop); - RenderThread.Start(); + _renderThread.Start(); while (Exists && !IsExiting) { @@ -113,11 +113,11 @@ namespace Ryujinx { UpdateFrame(); - if (TitleEvent) + if (_titleEvent) { - TitleEvent = false; + _titleEvent = false; - Title = NewTitle; + Title = _newTitle; } } @@ -128,94 +128,94 @@ namespace Ryujinx private new void UpdateFrame() { - HidControllerButtons CurrentButton = 0; - HidJoystickPosition LeftJoystick; - HidJoystickPosition RightJoystick; + HidControllerButtons currentButton = 0; + HidJoystickPosition leftJoystick; + HidJoystickPosition rightJoystick; - int LeftJoystickDX = 0; - int LeftJoystickDY = 0; - int RightJoystickDX = 0; - int RightJoystickDY = 0; + int leftJoystickDx = 0; + int leftJoystickDy = 0; + int rightJoystickDx = 0; + int rightJoystickDy = 0; //Keyboard Input - if (Keyboard.HasValue) + if (_keyboard.HasValue) { - KeyboardState Keyboard = this.Keyboard.Value; + KeyboardState keyboard = _keyboard.Value; - CurrentButton = Config.JoyConKeyboard.GetButtons(Keyboard); + currentButton = Config.JoyConKeyboard.GetButtons(keyboard); - (LeftJoystickDX, LeftJoystickDY) = Config.JoyConKeyboard.GetLeftStick(Keyboard); + (leftJoystickDx, leftJoystickDy) = Config.JoyConKeyboard.GetLeftStick(keyboard); - (RightJoystickDX, RightJoystickDY) = Config.JoyConKeyboard.GetRightStick(Keyboard); + (rightJoystickDx, rightJoystickDy) = Config.JoyConKeyboard.GetRightStick(keyboard); } //Controller Input - CurrentButton |= Config.JoyConController.GetButtons(); + currentButton |= Config.JoyConController.GetButtons(); //Keyboard has priority stick-wise - if (LeftJoystickDX == 0 && LeftJoystickDY == 0) + if (leftJoystickDx == 0 && leftJoystickDy == 0) { - (LeftJoystickDX, LeftJoystickDY) = Config.JoyConController.GetLeftStick(); + (leftJoystickDx, leftJoystickDy) = Config.JoyConController.GetLeftStick(); } - if (RightJoystickDX == 0 && RightJoystickDY == 0) + if (rightJoystickDx == 0 && rightJoystickDy == 0) { - (RightJoystickDX, RightJoystickDY) = Config.JoyConController.GetRightStick(); + (rightJoystickDx, rightJoystickDy) = Config.JoyConController.GetRightStick(); } - LeftJoystick = new HidJoystickPosition + leftJoystick = new HidJoystickPosition { - DX = LeftJoystickDX, - DY = LeftJoystickDY + DX = leftJoystickDx, + DY = leftJoystickDy }; - RightJoystick = new HidJoystickPosition + rightJoystick = new HidJoystickPosition { - DX = RightJoystickDX, - DY = RightJoystickDY + DX = rightJoystickDx, + DY = rightJoystickDy }; - bool HasTouch = false; + bool hasTouch = false; //Get screen touch position from left mouse click //OpenTK always captures mouse events, even if out of focus, so check if window is focused. - if (Focused && Mouse?.LeftButton == ButtonState.Pressed) + if (Focused && _mouse?.LeftButton == ButtonState.Pressed) { - MouseState Mouse = this.Mouse.Value; + MouseState mouse = _mouse.Value; - int ScrnWidth = Width; - int ScrnHeight = Height; + int scrnWidth = Width; + int scrnHeight = Height; if (Width > (Height * TouchScreenWidth) / TouchScreenHeight) { - ScrnWidth = (Height * TouchScreenWidth) / TouchScreenHeight; + scrnWidth = (Height * TouchScreenWidth) / TouchScreenHeight; } else { - ScrnHeight = (Width * TouchScreenHeight) / TouchScreenWidth; + scrnHeight = (Width * TouchScreenHeight) / TouchScreenWidth; } - int StartX = (Width - ScrnWidth) >> 1; - int StartY = (Height - ScrnHeight) >> 1; + int startX = (Width - scrnWidth) >> 1; + int startY = (Height - scrnHeight) >> 1; - int EndX = StartX + ScrnWidth; - int EndY = StartY + ScrnHeight; + int endX = startX + scrnWidth; + int endY = startY + scrnHeight; - if (Mouse.X >= StartX && - Mouse.Y >= StartY && - Mouse.X < EndX && - Mouse.Y < EndY) + if (mouse.X >= startX && + mouse.Y >= startY && + mouse.X < endX && + mouse.Y < endY) { - int ScrnMouseX = Mouse.X - StartX; - int ScrnMouseY = Mouse.Y - StartY; + int scrnMouseX = mouse.X - startX; + int scrnMouseY = mouse.Y - startY; - int MX = (ScrnMouseX * TouchScreenWidth) / ScrnWidth; - int MY = (ScrnMouseY * TouchScreenHeight) / ScrnHeight; + int mX = (scrnMouseX * TouchScreenWidth) / scrnWidth; + int mY = (scrnMouseY * TouchScreenHeight) / scrnHeight; - HidTouchPoint CurrentPoint = new HidTouchPoint + HidTouchPoint currentPoint = new HidTouchPoint { - X = MX, - Y = MY, + X = mX, + Y = mY, //Placeholder values till more data is acquired DiameterX = 10, @@ -223,73 +223,76 @@ namespace Ryujinx Angle = 90 }; - HasTouch = true; + hasTouch = true; - Device.Hid.SetTouchPoints(CurrentPoint); + _device.Hid.SetTouchPoints(currentPoint); } } - if (!HasTouch) + if (!hasTouch) { - Device.Hid.SetTouchPoints(); + _device.Hid.SetTouchPoints(); } - Device.Hid.SetJoyconButton( + _device.Hid.SetJoyconButton( HidControllerId.CONTROLLER_HANDHELD, HidControllerLayouts.Handheld_Joined, - CurrentButton, - LeftJoystick, - RightJoystick); + currentButton, + leftJoystick, + rightJoystick); - Device.Hid.SetJoyconButton( + _device.Hid.SetJoyconButton( HidControllerId.CONTROLLER_HANDHELD, HidControllerLayouts.Main, - CurrentButton, - LeftJoystick, - RightJoystick); + currentButton, + leftJoystick, + rightJoystick); } private new void RenderFrame() { - Renderer.RenderTarget.Render(); + _renderer.RenderTarget.Render(); - Device.Statistics.RecordSystemFrameTime(); + _device.Statistics.RecordSystemFrameTime(); - double HostFps = Device.Statistics.GetSystemFrameRate(); - double GameFps = Device.Statistics.GetGameFrameRate(); + double hostFps = _device.Statistics.GetSystemFrameRate(); + double gameFps = _device.Statistics.GetGameFrameRate(); - NewTitle = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0} | Game Vsync: " + - (Device.EnableDeviceVsync ? "On" : "Off"); + string titleSection = string.IsNullOrWhiteSpace(_device.System.CurrentTitle) ? string.Empty + : " | " + _device.System.CurrentTitle; - TitleEvent = true; + _newTitle = $"Ryujinx{titleSection} | Host FPS: {hostFps:0.0} | Game FPS: {gameFps:0.0} | " + + $"Game Vsync: {(_device.EnableDeviceVsync ? "On" : "Off")}"; + + _titleEvent = true; SwapBuffers(); - Device.System.SignalVsync(); + _device.System.SignalVsync(); - Device.VsyncEvent.Set(); + _device.VsyncEvent.Set(); } protected override void OnUnload(EventArgs e) { - RenderThread.Join(); + _renderThread.Join(); base.OnUnload(e); } protected override void OnResize(EventArgs e) { - ResizeEvent = true; + _resizeEvent = true; } protected override void OnKeyDown(KeyboardKeyEventArgs e) { - bool ToggleFullscreen = e.Key == Key.F11 || + bool toggleFullscreen = e.Key == Key.F11 || (e.Modifiers.HasFlag(KeyModifiers.Alt) && e.Key == Key.Enter); if (WindowState == WindowState.Fullscreen) { - if (e.Key == Key.Escape || ToggleFullscreen) + if (e.Key == Key.Escape || toggleFullscreen) { WindowState = WindowState.Normal; } @@ -301,33 +304,33 @@ namespace Ryujinx Exit(); } - if (ToggleFullscreen) + if (toggleFullscreen) { WindowState = WindowState.Fullscreen; } } - Keyboard = e.Keyboard; + _keyboard = e.Keyboard; } protected override void OnKeyUp(KeyboardKeyEventArgs e) { - Keyboard = e.Keyboard; + _keyboard = e.Keyboard; } protected override void OnMouseDown(MouseButtonEventArgs e) { - Mouse = e.Mouse; + _mouse = e.Mouse; } protected override void OnMouseUp(MouseButtonEventArgs e) { - Mouse = e.Mouse; + _mouse = e.Mouse; } protected override void OnMouseMove(MouseMoveEventArgs e) { - Mouse = e.Mouse; + _mouse = e.Mouse; } } } \ No newline at end of file diff --git a/Ryujinx/Ui/JoyConController.cs b/Ryujinx/Ui/JoyConController.cs index aac8efd7c4..28b631b235 100644 --- a/Ryujinx/Ui/JoyConController.cs +++ b/Ryujinx/Ui/JoyConController.cs @@ -5,7 +5,7 @@ using System; namespace Ryujinx.UI.Input { - public enum ControllerInputID + public enum ControllerInputId { Invalid, @@ -34,28 +34,28 @@ namespace Ryujinx.UI.Input public struct JoyConControllerLeft { - public ControllerInputID Stick; - public ControllerInputID StickButton; - public ControllerInputID DPadUp; - public ControllerInputID DPadDown; - public ControllerInputID DPadLeft; - public ControllerInputID DPadRight; - public ControllerInputID ButtonMinus; - public ControllerInputID ButtonL; - public ControllerInputID ButtonZL; + public ControllerInputId Stick; + public ControllerInputId StickButton; + public ControllerInputId DPadUp; + public ControllerInputId DPadDown; + public ControllerInputId DPadLeft; + public ControllerInputId DPadRight; + public ControllerInputId ButtonMinus; + public ControllerInputId ButtonL; + public ControllerInputId ButtonZl; } public struct JoyConControllerRight { - public ControllerInputID Stick; - public ControllerInputID StickButton; - public ControllerInputID ButtonA; - public ControllerInputID ButtonB; - public ControllerInputID ButtonX; - public ControllerInputID ButtonY; - public ControllerInputID ButtonPlus; - public ControllerInputID ButtonR; - public ControllerInputID ButtonZR; + public ControllerInputId Stick; + public ControllerInputId StickButton; + public ControllerInputId ButtonA; + public ControllerInputId ButtonB; + public ControllerInputId ButtonX; + public ControllerInputId ButtonY; + public ControllerInputId ButtonPlus; + public ControllerInputId ButtonR; + public ControllerInputId ButtonZr; } public class JoyConController @@ -69,24 +69,24 @@ namespace Ryujinx.UI.Input public JoyConControllerRight Right { private set; get; } public JoyConController( - bool Enabled, - int Index, - float Deadzone, - float TriggerThreshold, - JoyConControllerLeft Left, - JoyConControllerRight Right) + bool enabled, + int index, + float deadzone, + float triggerThreshold, + JoyConControllerLeft left, + JoyConControllerRight right) { - this.Enabled = Enabled; - this.Index = Index; - this.Deadzone = Deadzone; - this.TriggerThreshold = TriggerThreshold; - this.Left = Left; - this.Right = Right; + Enabled = enabled; + Index = index; + Deadzone = deadzone; + TriggerThreshold = triggerThreshold; + Left = left; + Right = right; //Unmapped controllers are problematic, skip them - if (GamePad.GetName(Index) == "Unmapped Controller") + if (GamePad.GetName(index) == "Unmapped Controller") { - this.Enabled = false; + Enabled = false; } } @@ -97,29 +97,29 @@ namespace Ryujinx.UI.Input return 0; } - GamePadState GpState = GamePad.GetState(Index); + GamePadState gpState = GamePad.GetState(Index); - HidControllerButtons Buttons = 0; + HidControllerButtons buttons = 0; - if (IsPressed(GpState, Left.DPadUp)) Buttons |= HidControllerButtons.KEY_DUP; - if (IsPressed(GpState, Left.DPadDown)) Buttons |= HidControllerButtons.KEY_DDOWN; - if (IsPressed(GpState, Left.DPadLeft)) Buttons |= HidControllerButtons.KEY_DLEFT; - if (IsPressed(GpState, Left.DPadRight)) Buttons |= HidControllerButtons.KEY_DRIGHT; - if (IsPressed(GpState, Left.StickButton)) Buttons |= HidControllerButtons.KEY_LSTICK; - if (IsPressed(GpState, Left.ButtonMinus)) Buttons |= HidControllerButtons.KEY_MINUS; - if (IsPressed(GpState, Left.ButtonL)) Buttons |= HidControllerButtons.KEY_L; - if (IsPressed(GpState, Left.ButtonZL)) Buttons |= HidControllerButtons.KEY_ZL; + if (IsPressed(gpState, Left.DPadUp)) buttons |= HidControllerButtons.KEY_DUP; + if (IsPressed(gpState, Left.DPadDown)) buttons |= HidControllerButtons.KEY_DDOWN; + if (IsPressed(gpState, Left.DPadLeft)) buttons |= HidControllerButtons.KEY_DLEFT; + if (IsPressed(gpState, Left.DPadRight)) buttons |= HidControllerButtons.KEY_DRIGHT; + if (IsPressed(gpState, Left.StickButton)) buttons |= HidControllerButtons.KEY_LSTICK; + if (IsPressed(gpState, Left.ButtonMinus)) buttons |= HidControllerButtons.KEY_MINUS; + if (IsPressed(gpState, Left.ButtonL)) buttons |= HidControllerButtons.KEY_L; + if (IsPressed(gpState, Left.ButtonZl)) buttons |= HidControllerButtons.KEY_ZL; - if (IsPressed(GpState, Right.ButtonA)) Buttons |= HidControllerButtons.KEY_A; - if (IsPressed(GpState, Right.ButtonB)) Buttons |= HidControllerButtons.KEY_B; - if (IsPressed(GpState, Right.ButtonX)) Buttons |= HidControllerButtons.KEY_X; - if (IsPressed(GpState, Right.ButtonY)) Buttons |= HidControllerButtons.KEY_Y; - if (IsPressed(GpState, Right.StickButton)) Buttons |= HidControllerButtons.KEY_RSTICK; - if (IsPressed(GpState, Right.ButtonPlus)) Buttons |= HidControllerButtons.KEY_PLUS; - if (IsPressed(GpState, Right.ButtonR)) Buttons |= HidControllerButtons.KEY_R; - if (IsPressed(GpState, Right.ButtonZR)) Buttons |= HidControllerButtons.KEY_ZR; + if (IsPressed(gpState, Right.ButtonA)) buttons |= HidControllerButtons.KEY_A; + if (IsPressed(gpState, Right.ButtonB)) buttons |= HidControllerButtons.KEY_B; + if (IsPressed(gpState, Right.ButtonX)) buttons |= HidControllerButtons.KEY_X; + if (IsPressed(gpState, Right.ButtonY)) buttons |= HidControllerButtons.KEY_Y; + if (IsPressed(gpState, Right.StickButton)) buttons |= HidControllerButtons.KEY_RSTICK; + if (IsPressed(gpState, Right.ButtonPlus)) buttons |= HidControllerButtons.KEY_PLUS; + if (IsPressed(gpState, Right.ButtonR)) buttons |= HidControllerButtons.KEY_R; + if (IsPressed(gpState, Right.ButtonZr)) buttons |= HidControllerButtons.KEY_ZR; - return Buttons; + return buttons; } public (short, short) GetLeftStick() @@ -142,71 +142,71 @@ namespace Ryujinx.UI.Input return GetStick(Right.Stick); } - private (short, short) GetStick(ControllerInputID Joystick) + private (short, short) GetStick(ControllerInputId joystick) { - GamePadState GpState = GamePad.GetState(Index); + GamePadState gpState = GamePad.GetState(Index); - switch (Joystick) + switch (joystick) { - case ControllerInputID.LJoystick: - return ApplyDeadzone(GpState.ThumbSticks.Left); + case ControllerInputId.LJoystick: + return ApplyDeadzone(gpState.ThumbSticks.Left); - case ControllerInputID.RJoystick: - return ApplyDeadzone(GpState.ThumbSticks.Right); + case ControllerInputId.RJoystick: + return ApplyDeadzone(gpState.ThumbSticks.Right); default: return (0, 0); } } - private (short, short) ApplyDeadzone(Vector2 Axis) + private (short, short) ApplyDeadzone(Vector2 axis) { - return (ClampAxis(MathF.Abs(Axis.X) > Deadzone ? Axis.X : 0f), - ClampAxis(MathF.Abs(Axis.Y) > Deadzone ? Axis.Y : 0f)); + return (ClampAxis(MathF.Abs(axis.X) > Deadzone ? axis.X : 0f), + ClampAxis(MathF.Abs(axis.Y) > Deadzone ? axis.Y : 0f)); } - private static short ClampAxis(float Value) + private static short ClampAxis(float value) { - if (Value <= -short.MaxValue) + if (value <= -short.MaxValue) { return -short.MaxValue; } else { - return (short)(Value * short.MaxValue); + return (short)(value * short.MaxValue); } } - private bool IsPressed(GamePadState GpState, ControllerInputID Button) + private bool IsPressed(GamePadState gpState, ControllerInputId button) { - switch (Button) + switch (button) { - case ControllerInputID.A: return GpState.Buttons.A == ButtonState.Pressed; - case ControllerInputID.B: return GpState.Buttons.B == ButtonState.Pressed; - case ControllerInputID.X: return GpState.Buttons.X == ButtonState.Pressed; - case ControllerInputID.Y: return GpState.Buttons.Y == ButtonState.Pressed; - case ControllerInputID.LStick: return GpState.Buttons.LeftStick == ButtonState.Pressed; - case ControllerInputID.RStick: return GpState.Buttons.RightStick == ButtonState.Pressed; - case ControllerInputID.LShoulder: return GpState.Buttons.LeftShoulder == ButtonState.Pressed; - case ControllerInputID.RShoulder: return GpState.Buttons.RightShoulder == ButtonState.Pressed; - case ControllerInputID.DPadUp: return GpState.DPad.Up == ButtonState.Pressed; - case ControllerInputID.DPadDown: return GpState.DPad.Down == ButtonState.Pressed; - case ControllerInputID.DPadLeft: return GpState.DPad.Left == ButtonState.Pressed; - case ControllerInputID.DPadRight: return GpState.DPad.Right == ButtonState.Pressed; - case ControllerInputID.Start: return GpState.Buttons.Start == ButtonState.Pressed; - case ControllerInputID.Back: return GpState.Buttons.Back == ButtonState.Pressed; + case ControllerInputId.A: return gpState.Buttons.A == ButtonState.Pressed; + case ControllerInputId.B: return gpState.Buttons.B == ButtonState.Pressed; + case ControllerInputId.X: return gpState.Buttons.X == ButtonState.Pressed; + case ControllerInputId.Y: return gpState.Buttons.Y == ButtonState.Pressed; + case ControllerInputId.LStick: return gpState.Buttons.LeftStick == ButtonState.Pressed; + case ControllerInputId.RStick: return gpState.Buttons.RightStick == ButtonState.Pressed; + case ControllerInputId.LShoulder: return gpState.Buttons.LeftShoulder == ButtonState.Pressed; + case ControllerInputId.RShoulder: return gpState.Buttons.RightShoulder == ButtonState.Pressed; + case ControllerInputId.DPadUp: return gpState.DPad.Up == ButtonState.Pressed; + case ControllerInputId.DPadDown: return gpState.DPad.Down == ButtonState.Pressed; + case ControllerInputId.DPadLeft: return gpState.DPad.Left == ButtonState.Pressed; + case ControllerInputId.DPadRight: return gpState.DPad.Right == ButtonState.Pressed; + case ControllerInputId.Start: return gpState.Buttons.Start == ButtonState.Pressed; + case ControllerInputId.Back: return gpState.Buttons.Back == ButtonState.Pressed; - case ControllerInputID.LTrigger: return GpState.Triggers.Left >= TriggerThreshold; - case ControllerInputID.RTrigger: return GpState.Triggers.Right >= TriggerThreshold; + case ControllerInputId.LTrigger: return gpState.Triggers.Left >= TriggerThreshold; + case ControllerInputId.RTrigger: return gpState.Triggers.Right >= TriggerThreshold; //Using thumbsticks as buttons is not common, but it would be nice not to ignore them - case ControllerInputID.LJoystick: - return GpState.ThumbSticks.Left.X >= Deadzone || - GpState.ThumbSticks.Left.Y >= Deadzone; + case ControllerInputId.LJoystick: + return gpState.ThumbSticks.Left.X >= Deadzone || + gpState.ThumbSticks.Left.Y >= Deadzone; - case ControllerInputID.RJoystick: - return GpState.ThumbSticks.Right.X >= Deadzone || - GpState.ThumbSticks.Right.Y >= Deadzone; + case ControllerInputId.RJoystick: + return gpState.ThumbSticks.Right.X >= Deadzone || + gpState.ThumbSticks.Right.Y >= Deadzone; default: return false; diff --git a/Ryujinx/Ui/JoyConKeyboard.cs b/Ryujinx/Ui/JoyConKeyboard.cs index ea9645539a..fd399fe8d1 100644 --- a/Ryujinx/Ui/JoyConKeyboard.cs +++ b/Ryujinx/Ui/JoyConKeyboard.cs @@ -16,7 +16,7 @@ namespace Ryujinx.UI.Input public int DPadRight; public int ButtonMinus; public int ButtonL; - public int ButtonZL; + public int ButtonZl; } public struct JoyConKeyboardRight @@ -32,7 +32,7 @@ namespace Ryujinx.UI.Input public int ButtonY; public int ButtonPlus; public int ButtonR; - public int ButtonZR; + public int ButtonZr; } public class JoyConKeyboard @@ -41,62 +41,62 @@ namespace Ryujinx.UI.Input public JoyConKeyboardRight Right; public JoyConKeyboard( - JoyConKeyboardLeft Left, - JoyConKeyboardRight Right) + JoyConKeyboardLeft left, + JoyConKeyboardRight right) { - this.Left = Left; - this.Right = Right; + Left = left; + Right = right; } - public HidControllerButtons GetButtons(KeyboardState Keyboard) + public HidControllerButtons GetButtons(KeyboardState keyboard) { - HidControllerButtons Buttons = 0; + HidControllerButtons buttons = 0; - if (Keyboard[(Key)Left.StickButton]) Buttons |= HidControllerButtons.KEY_LSTICK; - if (Keyboard[(Key)Left.DPadUp]) Buttons |= HidControllerButtons.KEY_DUP; - if (Keyboard[(Key)Left.DPadDown]) Buttons |= HidControllerButtons.KEY_DDOWN; - if (Keyboard[(Key)Left.DPadLeft]) Buttons |= HidControllerButtons.KEY_DLEFT; - if (Keyboard[(Key)Left.DPadRight]) Buttons |= HidControllerButtons.KEY_DRIGHT; - if (Keyboard[(Key)Left.ButtonMinus]) Buttons |= HidControllerButtons.KEY_MINUS; - if (Keyboard[(Key)Left.ButtonL]) Buttons |= HidControllerButtons.KEY_L; - if (Keyboard[(Key)Left.ButtonZL]) Buttons |= HidControllerButtons.KEY_ZL; + if (keyboard[(Key)Left.StickButton]) buttons |= HidControllerButtons.KEY_LSTICK; + if (keyboard[(Key)Left.DPadUp]) buttons |= HidControllerButtons.KEY_DUP; + if (keyboard[(Key)Left.DPadDown]) buttons |= HidControllerButtons.KEY_DDOWN; + if (keyboard[(Key)Left.DPadLeft]) buttons |= HidControllerButtons.KEY_DLEFT; + if (keyboard[(Key)Left.DPadRight]) buttons |= HidControllerButtons.KEY_DRIGHT; + if (keyboard[(Key)Left.ButtonMinus]) buttons |= HidControllerButtons.KEY_MINUS; + if (keyboard[(Key)Left.ButtonL]) buttons |= HidControllerButtons.KEY_L; + if (keyboard[(Key)Left.ButtonZl]) buttons |= HidControllerButtons.KEY_ZL; - if (Keyboard[(Key)Right.StickButton]) Buttons |= HidControllerButtons.KEY_RSTICK; - if (Keyboard[(Key)Right.ButtonA]) Buttons |= HidControllerButtons.KEY_A; - if (Keyboard[(Key)Right.ButtonB]) Buttons |= HidControllerButtons.KEY_B; - if (Keyboard[(Key)Right.ButtonX]) Buttons |= HidControllerButtons.KEY_X; - if (Keyboard[(Key)Right.ButtonY]) Buttons |= HidControllerButtons.KEY_Y; - if (Keyboard[(Key)Right.ButtonPlus]) Buttons |= HidControllerButtons.KEY_PLUS; - if (Keyboard[(Key)Right.ButtonR]) Buttons |= HidControllerButtons.KEY_R; - if (Keyboard[(Key)Right.ButtonZR]) Buttons |= HidControllerButtons.KEY_ZR; + if (keyboard[(Key)Right.StickButton]) buttons |= HidControllerButtons.KEY_RSTICK; + if (keyboard[(Key)Right.ButtonA]) buttons |= HidControllerButtons.KEY_A; + if (keyboard[(Key)Right.ButtonB]) buttons |= HidControllerButtons.KEY_B; + if (keyboard[(Key)Right.ButtonX]) buttons |= HidControllerButtons.KEY_X; + if (keyboard[(Key)Right.ButtonY]) buttons |= HidControllerButtons.KEY_Y; + if (keyboard[(Key)Right.ButtonPlus]) buttons |= HidControllerButtons.KEY_PLUS; + if (keyboard[(Key)Right.ButtonR]) buttons |= HidControllerButtons.KEY_R; + if (keyboard[(Key)Right.ButtonZr]) buttons |= HidControllerButtons.KEY_ZR; - return Buttons; + return buttons; } - public (short, short) GetLeftStick(KeyboardState Keyboard) + public (short, short) GetLeftStick(KeyboardState keyboard) { - short DX = 0; - short DY = 0; + short dx = 0; + short dy = 0; - if (Keyboard[(Key)Left.StickUp]) DY = short.MaxValue; - if (Keyboard[(Key)Left.StickDown]) DY = -short.MaxValue; - if (Keyboard[(Key)Left.StickLeft]) DX = -short.MaxValue; - if (Keyboard[(Key)Left.StickRight]) DX = short.MaxValue; + if (keyboard[(Key)Left.StickUp]) dy = short.MaxValue; + if (keyboard[(Key)Left.StickDown]) dy = -short.MaxValue; + if (keyboard[(Key)Left.StickLeft]) dx = -short.MaxValue; + if (keyboard[(Key)Left.StickRight]) dx = short.MaxValue; - return (DX, DY); + return (dx, dy); } - public (short, short) GetRightStick(KeyboardState Keyboard) + public (short, short) GetRightStick(KeyboardState keyboard) { - short DX = 0; - short DY = 0; + short dx = 0; + short dy = 0; - if (Keyboard[(Key)Right.StickUp]) DY = short.MaxValue; - if (Keyboard[(Key)Right.StickDown]) DY = -short.MaxValue; - if (Keyboard[(Key)Right.StickLeft]) DX = -short.MaxValue; - if (Keyboard[(Key)Right.StickRight]) DX = short.MaxValue; + if (keyboard[(Key)Right.StickUp]) dy = short.MaxValue; + if (keyboard[(Key)Right.StickDown]) dy = -short.MaxValue; + if (keyboard[(Key)Right.StickLeft]) dx = -short.MaxValue; + if (keyboard[(Key)Right.StickRight]) dx = short.MaxValue; - return (DX, DY); + return (dx, dy); } } } From 57600657d75870db36af7e8cf1c573ee0a966822 Mon Sep 17 00:00:00 2001 From: HorrorTroll Date: Sun, 18 Nov 2018 17:00:30 +0700 Subject: [PATCH 2/2] Set theme jekyll-theme-cayman --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000000..c4192631f2 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file