From 07c0bdb8e8a356fd9bad3356c32209bbb815695a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com>
Date: Tue, 10 Dec 2024 17:20:59 +0200
Subject: [PATCH] Fix operand access being wrong about conditional access

---
 tests/src/tests/tests.decoder.cpp         | 44 +++++++++++++++++++++
 tests/src/tests/tests.packed.cpp          | 47 ++++++++++++++++++++++-
 zasm/include/zasm/core/packed.hpp         |  4 +-
 zasm/include/zasm/program/instruction.hpp |  2 +-
 4 files changed, 93 insertions(+), 4 deletions(-)

diff --git a/tests/src/tests/tests.decoder.cpp b/tests/src/tests/tests.decoder.cpp
index 22cafb7..c1e22d1 100644
--- a/tests/src/tests/tests.decoder.cpp
+++ b/tests/src/tests/tests.decoder.cpp
@@ -78,4 +78,48 @@ namespace zasm::tests
         }
     }
 
+    TEST(DecoderTests, Issue_149)
+    {
+        Program program(MachineMode::AMD64);
+        Decoder decoder(MachineMode::AMD64);
+
+        const std::array<uint8_t, 8> inputBytes = {
+            0xF0, 0x0F, 0xB1, 0x95, 0x74, 0x17, 0x01, 0x00, // lock cmpxchg dword ptr ss:[rbp+0x11774], edx
+        };
+
+        auto decoded = decoder.decode(inputBytes.data(), inputBytes.size(), 0x00400000);
+        ASSERT_TRUE(decoded);
+
+        ASSERT_EQ(decoded->getMnemonic(), x86::Mnemonic::Cmpxchg);
+
+        ASSERT_EQ(decoded->getOperandCount(), 4);
+
+        ASSERT_EQ(decoded->isOperandType<Mem>(0), true);
+        ASSERT_EQ(decoded->getOperand<Mem>(0).getBase(), x86::rbp);
+        ASSERT_EQ(decoded->getOperand<Mem>(0).getDisplacement(), 0x11774);
+        ASSERT_EQ(decoded->isOperandRead(0), true);
+        ASSERT_EQ(decoded->isOperandWrite(0), true);
+        ASSERT_EQ(decoded->isOperandCondWrite(0), true);
+
+        ASSERT_EQ(decoded->isOperandType<Reg>(1), true);
+        ASSERT_EQ(decoded->getOperand<Reg>(1), x86::edx);
+        ASSERT_EQ(decoded->isOperandRead(1), true);
+        ASSERT_EQ(decoded->isOperandCondRead(1), false);
+        ASSERT_EQ(decoded->isOperandWrite(1), false);
+        ASSERT_EQ(decoded->isOperandCondWrite(1), false);
+
+        ASSERT_EQ(decoded->isOperandType<Reg>(2), true);
+        ASSERT_EQ(decoded->getOperand<Reg>(2), x86::eax);
+        ASSERT_EQ(decoded->isOperandRead(2), true);
+        ASSERT_EQ(decoded->isOperandWrite(2), true);
+        ASSERT_EQ(decoded->isOperandCondWrite(2), true);
+
+        ASSERT_EQ(decoded->isOperandType<Reg>(3), true);
+        ASSERT_EQ(decoded->getOperand<Reg>(3), x86::rflags);
+        ASSERT_EQ(decoded->isOperandRead(3), false);
+        ASSERT_EQ(decoded->isOperandCondRead(3), false);
+        ASSERT_EQ(decoded->isOperandWrite(3), true);
+        ASSERT_EQ(decoded->isOperandCondWrite(3), false);
+    }
+
 } // namespace zasm::tests
diff --git a/tests/src/tests/tests.packed.cpp b/tests/src/tests/tests.packed.cpp
index 46d4424..4c0d91f 100644
--- a/tests/src/tests/tests.packed.cpp
+++ b/tests/src/tests/tests.packed.cpp
@@ -81,4 +81,49 @@ namespace zasm::tests
         ASSERT_EQ(vis.get<8>(), Operand::Visibility::Invalid);
         ASSERT_EQ(vis.get<9>(), Operand::Visibility::Invalid);
     }
-} // namespace zasm::tests
+
+    TEST(PackedTests, TestPackOperandAccess)
+    {
+        using PackedOperandAccess = Packed<uint64_t, Operand::Access, 4>;
+        static_assert(sizeof(PackedOperandAccess) == sizeof(uint64_t));
+
+        PackedOperandAccess access{};
+        ASSERT_EQ(access.size(), 16);
+        ASSERT_EQ(access.capacity(), 16);
+
+        ASSERT_EQ(access.get<0>(), Operand::Access::None);
+        ASSERT_EQ(access.get<1>(), Operand::Access::None);
+        ASSERT_EQ(access.get<2>(), Operand::Access::None);
+        ASSERT_EQ(access.get<3>(), Operand::Access::None);
+        ASSERT_EQ(access.get<4>(), Operand::Access::None);
+        ASSERT_EQ(access.get<5>(), Operand::Access::None);
+        ASSERT_EQ(access.get<6>(), Operand::Access::None);
+        ASSERT_EQ(access.get<7>(), Operand::Access::None);
+        ASSERT_EQ(access.get<8>(), Operand::Access::None);
+        ASSERT_EQ(access.get<9>(), Operand::Access::None);
+        ASSERT_EQ(access.get<10>(), Operand::Access::None);
+
+        access.set<0>(Operand::Access::Read);
+        ASSERT_EQ(access.get<0>(), Operand::Access::Read);
+
+        access.set<5>(Operand::Access::Write);
+        ASSERT_EQ(access.get<0>(), Operand::Access::Read);
+        ASSERT_EQ(access.get<5>(), Operand::Access::Write);
+
+        access.set<10>(Operand::Access::CondRead);
+        ASSERT_EQ(access.get<0>(), Operand::Access::Read);
+
+        access.set<15>(Operand::Access::CondWrite);
+        ASSERT_EQ(access.get<0>(), Operand::Access::Read);
+        ASSERT_EQ(access.get<5>(), Operand::Access::Write);
+        ASSERT_EQ(access.get<10>(), Operand::Access::CondRead);
+        ASSERT_EQ(access.get<15>(), Operand::Access::CondWrite);
+
+        access.set<15>(Operand::Access::None);
+        ASSERT_EQ(access.get<0>(), Operand::Access::Read);
+        ASSERT_EQ(access.get<5>(), Operand::Access::Write);
+        ASSERT_EQ(access.get<10>(), Operand::Access::CondRead);
+        ASSERT_EQ(access.get<15>(), Operand::Access::None);    
+    }
+
+    } // namespace zasm::tests
diff --git a/zasm/include/zasm/core/packed.hpp b/zasm/include/zasm/core/packed.hpp
index 44f1d45..34f9a44 100644
--- a/zasm/include/zasm/core/packed.hpp
+++ b/zasm/include/zasm/core/packed.hpp
@@ -54,7 +54,7 @@ namespace zasm
         template<std::size_t TIndex> constexpr void set(const TElement& val) noexcept
         {
             constexpr auto bitIndex = (TIndex * TElementBitSize);
-            static_assert(bitIndex + TElementBitSize < kStorageBitSize);
+            static_assert(bitIndex + TElementBitSize <= kStorageBitSize);
 
             const auto newVal = (static_cast<TUnderlying>(val) & kElementMask) << bitIndex;
 
@@ -81,7 +81,7 @@ namespace zasm
         template<std::size_t TIndex> constexpr TElement get() const noexcept
         {
             constexpr auto bitIndex = (TIndex * TElementBitSize);
-            static_assert(bitIndex + TElementBitSize < kStorageBitSize);
+            static_assert(bitIndex + TElementBitSize <= kStorageBitSize);
 
             const auto res = (_data >> bitIndex) & kElementMask;
             return static_cast<TElement>(res);
diff --git a/zasm/include/zasm/program/instruction.hpp b/zasm/include/zasm/program/instruction.hpp
index ed861c2..e393ca4 100644
--- a/zasm/include/zasm/program/instruction.hpp
+++ b/zasm/include/zasm/program/instruction.hpp
@@ -67,7 +67,7 @@ namespace zasm
     public:
         static constexpr auto kInstrType = InstructionBase::Type::Detail;
 
-        using OperandsAccess = Packed<std::uint32_t, Operand::Access, 3>;
+        using OperandsAccess = Packed<std::uint64_t, Operand::Access, 4>;
         using OperandsVisibility = Packed<std::uint32_t, Operand::Visibility, 3>;
 
         struct CPUFlags