From 48a1790d812efe6abaf9c217962438f82b2525b7 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Mon, 10 Mar 2014 15:59:32 +0100 Subject: [PATCH] Common: Add a generic class for accessing bitfields in a fast and endianness-independent way. The underlying storage type of a bitfield can be any intrinsic integer type, but also any enumeration. Custom storage types are supported if the following things are defined on the storage type: - casting 0 to the storage type - bit shift operators (in both directions) - bitwise & operator - bitwise ~ operator - std::make_unsigned specialization --- Source/Core/Common/BitField.h | 160 ++++++++++++++++++++++ Source/Core/Common/Common.vcxproj | 3 +- Source/Core/Common/Common.vcxproj.filters | 3 +- 3 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 Source/Core/Common/BitField.h diff --git a/Source/Core/Common/BitField.h b/Source/Core/Common/BitField.h new file mode 100644 index 0000000000..40e748229e --- /dev/null +++ b/Source/Core/Common/BitField.h @@ -0,0 +1,160 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +// Copyright 2014 Tony Wasserka +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the owner nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#pragma once + +#include +#include + +/* + * Abstract bitfield class + * + * Allows endianness-independent access to individual bitfields within some raw + * integer value. The assembly generated by this class is identical to the + * usage of raw bitfields, so it's a perfectly fine replacement. + * + * For BitField, X is the distance of the bitfield to the LSB of the + * raw value, Y is the length in bits of the bitfield. Z is an integer type + * which determines the sign of the bitfield. Z must have the same size as the + * raw integer. + * + * + * General usage: + * + * Create a new union with the raw integer value as a member. + * Then for each bitfield you want to expose, add a BitField member + * in the union. The template parameters are the bit offset and the number + * of desired bits. + * + * Changes in the bitfield members will then get reflected in the raw integer + * value and vice-versa. + * + * + * Sample usage: + * + * union SomeRegister + * { + * u32 hex; + * + * BitField<0,7,u32> first_seven_bits; // unsigned + * BitField<7,8,32> next_eight_bits; // unsigned + * BitField<3,15,s32> some_signed_fields; // signed + * }; + * + * This is equivalent to the little-endian specific code: + * + * union SomeRegister + * { + * u32 hex; + * + * struct + * { + * u32 first_seven_bits : 7; + * u32 next_eight_bits : 8; + * }; + * struct + * { + * u32 : 3; // padding + * s32 some_signed_fields : 15; + * }; + * }; + * + * + * Caveats: + * + * BitField provides automatic casting from and to the storage type where + * appropriate. However, when using non-typesafe functions like printf, an + * explicit cast must be performed on the BitField object to make sure it gets + * passed correctly, e.g.: + * printf("Value: %d", (s32)some_register.some_signed_fields); + * + */ +template +struct BitField +{ +private: + // This constructor might be considered ambiguous: + // Would it initialize the storage or just the bitfield? + // Hence, delete it. Use the assignment operator to set bitfield values! + BitField(T val) = delete; + +public: + // Force default constructor to be created + // so that we can use this within unions + BitField() = default; + + BitField& operator=(T val) + { + storage = (storage & ~GetMask()) | ((val << position) & GetMask()); + return *this; + } + + operator T() const + { + if (std::numeric_limits::is_signed) + { + std::size_t shift = 8 * sizeof(T) - bits; + return (T)(((storage & GetMask()) << (shift - position)) >> shift); + } + else + { + return (T)((storage & GetMask()) >> position); + } + } + +private: + // StorageType is T for non-enum types and the underlying type of T if + // T is an enumeration. Note that T is wrapped within an enable_if in the + // former case to workaround compile errors which arise when using + // std::underlying_type::type directly. + typedef typename std::conditional::value, + std::underlying_type, + std::enable_if>::type::type StorageType; + + // Unsigned version of StorageType + typedef typename std::make_unsigned::type StorageTypeU; + + StorageType GetMask() const + { + return ((~(StorageTypeU)0) >> (8*sizeof(T) - bits)) << position; + } + + StorageType storage; + + static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); + + // And, you know, just in case people specify something stupid like bits=position=0x80000000 + static_assert(position < 8 * sizeof(T), "Invalid position"); + static_assert(bits <= 8 * sizeof(T), "Invalid number of bits"); + static_assert(bits > 0, "Invalid number of bits"); +}; diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 213e3c0765..f45ae50c12 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -46,6 +46,7 @@ + @@ -143,4 +144,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index e50e5ebfb8..a663f170ab 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -12,6 +12,7 @@ + @@ -110,4 +111,4 @@ - \ No newline at end of file +