diff --git a/src/audio_core/hle/shared_memory.h b/src/audio_core/hle/shared_memory.h index 440ab57e5..ed3aef349 100644 --- a/src/audio_core/hle/shared_memory.h +++ b/src/audio_core/hle/shared_memory.h @@ -126,29 +126,29 @@ struct SourceConfiguration { union { u32_le dirty_raw; - BitField<0, 1, u32_le> format_dirty; - BitField<1, 1, u32_le> mono_or_stereo_dirty; - BitField<2, 1, u32_le> adpcm_coefficients_dirty; + BitField<0, 1, u32> format_dirty; + BitField<1, 1, u32> mono_or_stereo_dirty; + BitField<2, 1, u32> adpcm_coefficients_dirty; /// Tends to be set when a looped buffer is queued. - BitField<3, 1, u32_le> partial_embedded_buffer_dirty; - BitField<4, 1, u32_le> partial_reset_flag; + BitField<3, 1, u32> partial_embedded_buffer_dirty; + BitField<4, 1, u32> partial_reset_flag; - BitField<16, 1, u32_le> enable_dirty; - BitField<17, 1, u32_le> interpolation_dirty; - BitField<18, 1, u32_le> rate_multiplier_dirty; - BitField<19, 1, u32_le> buffer_queue_dirty; - BitField<20, 1, u32_le> loop_related_dirty; + BitField<16, 1, u32> enable_dirty; + BitField<17, 1, u32> interpolation_dirty; + BitField<18, 1, u32> rate_multiplier_dirty; + BitField<19, 1, u32> buffer_queue_dirty; + BitField<20, 1, u32> loop_related_dirty; /// Tends to also be set when embedded buffer is updated. - BitField<21, 1, u32_le> play_position_dirty; - BitField<22, 1, u32_le> filters_enabled_dirty; - BitField<23, 1, u32_le> simple_filter_dirty; - BitField<24, 1, u32_le> biquad_filter_dirty; - BitField<25, 1, u32_le> gain_0_dirty; - BitField<26, 1, u32_le> gain_1_dirty; - BitField<27, 1, u32_le> gain_2_dirty; - BitField<28, 1, u32_le> sync_dirty; - BitField<29, 1, u32_le> reset_flag; - BitField<30, 1, u32_le> embedded_buffer_dirty; + BitField<21, 1, u32> play_position_dirty; + BitField<22, 1, u32> filters_enabled_dirty; + BitField<23, 1, u32> simple_filter_dirty; + BitField<24, 1, u32> biquad_filter_dirty; + BitField<25, 1, u32> gain_0_dirty; + BitField<26, 1, u32> gain_1_dirty; + BitField<27, 1, u32> gain_2_dirty; + BitField<28, 1, u32> sync_dirty; + BitField<29, 1, u32> reset_flag; + BitField<30, 1, u32> embedded_buffer_dirty; }; // Gain control @@ -206,8 +206,8 @@ struct SourceConfiguration { union { u16_le filters_enabled; - BitField<0, 1, u16_le> simple_filter_enabled; - BitField<1, 1, u16_le> biquad_filter_enabled; + BitField<0, 1, u16> simple_filter_enabled; + BitField<1, 1, u16> biquad_filter_enabled; }; SimpleFilter simple_filter; @@ -227,8 +227,8 @@ struct SourceConfiguration { /// ADPCM Predictor (4 bits) and Scale (4 bits) union { u16_le adpcm_ps; - BitField<0, 4, u16_le> adpcm_scale; - BitField<4, 4, u16_le> adpcm_predictor; + BitField<0, 4, u16> adpcm_scale; + BitField<4, 4, u16> adpcm_predictor; }; /// ADPCM Historical Samples (y[n-1] and y[n-2]) @@ -285,14 +285,14 @@ struct SourceConfiguration { u16_le flags1_raw; BitField<0, 2, MonoOrStereo> mono_or_stereo; BitField<2, 2, Format> format; - BitField<5, 1, u16_le> fade_in; + BitField<5, 1, u16> fade_in; }; /// ADPCM Predictor (4 bit) and Scale (4 bit) union { u16_le adpcm_ps; - BitField<0, 4, u16_le> adpcm_scale; - BitField<4, 4, u16_le> adpcm_predictor; + BitField<0, 4, u16> adpcm_scale; + BitField<4, 4, u16> adpcm_predictor; }; /// ADPCM Historical Samples (y[n-1] and y[n-2]) @@ -300,8 +300,8 @@ struct SourceConfiguration { union { u16_le flags2_raw; - BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed? - BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer? + BitField<0, 1, u16> adpcm_dirty; ///< Has the ADPCM info above been changed? + BitField<1, 1, u16> is_looping; ///< Is this a looping buffer? }; /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this @@ -334,20 +334,20 @@ struct DspConfiguration { union { u32_le dirty_raw; - BitField<8, 1, u32_le> mixer1_enabled_dirty; - BitField<9, 1, u32_le> mixer2_enabled_dirty; - BitField<10, 1, u32_le> delay_effect_0_dirty; - BitField<11, 1, u32_le> delay_effect_1_dirty; - BitField<12, 1, u32_le> reverb_effect_0_dirty; - BitField<13, 1, u32_le> reverb_effect_1_dirty; + BitField<8, 1, u32> mixer1_enabled_dirty; + BitField<9, 1, u32> mixer2_enabled_dirty; + BitField<10, 1, u32> delay_effect_0_dirty; + BitField<11, 1, u32> delay_effect_1_dirty; + BitField<12, 1, u32> reverb_effect_0_dirty; + BitField<13, 1, u32> reverb_effect_1_dirty; - BitField<16, 1, u32_le> volume_0_dirty; + BitField<16, 1, u32> volume_0_dirty; - BitField<24, 1, u32_le> volume_1_dirty; - BitField<25, 1, u32_le> volume_2_dirty; - BitField<26, 1, u32_le> output_format_dirty; - BitField<27, 1, u32_le> limiter_enabled_dirty; - BitField<28, 1, u32_le> headphones_connected_dirty; + BitField<24, 1, u32> volume_1_dirty; + BitField<25, 1, u32> volume_2_dirty; + BitField<26, 1, u32> output_format_dirty; + BitField<27, 1, u32> limiter_enabled_dirty; + BitField<28, 1, u32> headphones_connected_dirty; }; /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for @@ -384,9 +384,9 @@ struct DspConfiguration { /// The DSP clears these each audio frame. union { u16_le dirty_raw; - BitField<0, 1, u16_le> enable_dirty; - BitField<1, 1, u16_le> work_buffer_address_dirty; - BitField<2, 1, u16_le> other_dirty; ///< Set when anything else has been changed + BitField<0, 1, u16> enable_dirty; + BitField<1, 1, u16> work_buffer_address_dirty; + BitField<2, 1, u16> other_dirty; ///< Set when anything else has been changed }; u16_le enable; diff --git a/src/common/bit_field.h b/src/common/bit_field.h index e27c93907..02266c7b9 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -34,6 +34,7 @@ #include #include #include "common/common_funcs.h" +#include "common/swap.h" /* * Abstract bitfield class @@ -108,7 +109,7 @@ * symptoms. */ #pragma pack(1) -template +template struct BitField { private: // UnderlyingType is T for non-enum types and the underlying type of T if @@ -121,6 +122,8 @@ private: // We store the value as the unsigned type to avoid undefined behaviour on value shifting using StorageType = std::make_unsigned_t; + using StorageTypeWithEndian = typename AddEndian::type; + public: BitField& operator=(const BitField&) = default; @@ -168,7 +171,7 @@ public: } FORCE_INLINE void Assign(const T& value) { - storage = (storage & ~mask) | FormatValue(value); + storage = (static_cast(storage) & ~mask) | FormatValue(value); } FORCE_INLINE T Value() const { @@ -180,7 +183,7 @@ public: } private: - StorageType storage; + StorageTypeWithEndian storage; static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); @@ -196,3 +199,6 @@ private: static_assert(std::is_trivially_copyable>::value, "BitField must be trivially copyable"); #endif + +template +using BitFieldBE = BitField; diff --git a/src/common/swap.h b/src/common/swap.h index 466096f58..4b82865fe 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -172,7 +172,7 @@ struct swap_struct_t { using swapped_t = swap_struct_t; protected: - T value = T(); + T value; static T swap(T v) { return F::swap(v); @@ -645,64 +645,116 @@ protected: } }; +struct SwapTag {}; // Use the different endianness from the system +struct KeepTag {}; // Use the same endianness as the system + +template +struct AddEndian; + +// KeepTag specializations + +template +struct AddEndian { + using type = T; +}; + +// SwapTag specializations + +template <> +struct AddEndian { + using type = u8; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = s8; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template <> +struct AddEndian { + using type = swap_struct_t>; +}; + +template +struct AddEndian { + static_assert(std::is_enum_v); + using type = swap_enum_t; +}; + +// Alias LETag/BETag as KeepTag/SwapTag depending on the system #if COMMON_LITTLE_ENDIAN -using u16_le = u16; -using u32_le = u32; -using u64_le = u64; -using s16_le = s16; -using s32_le = s32; -using s64_le = s64; +using LETag = KeepTag; +using BETag = SwapTag; -template -using enum_le = std::enable_if_t, T>; - -using float_le = float; -using double_le = double; - -using u64_be = swap_struct_t>; -using s64_be = swap_struct_t>; - -using u32_be = swap_struct_t>; -using s32_be = swap_struct_t>; - -using u16_be = swap_struct_t>; -using s16_be = swap_struct_t>; - -template -using enum_be = swap_enum_t; - -using float_be = swap_struct_t>; -using double_be = swap_struct_t>; #else -using u64_le = swap_struct_t>; -using s64_le = swap_struct_t>; - -using u32_le = swap_struct_t>; -using s32_le = swap_struct_t>; - -using u16_le = swap_struct_t>; -using s16_le = swap_struct_t>; - -template -using enum_le = swap_enum_t; - -using float_le = swap_struct_t>; -using double_le = swap_struct_t>; - -using u16_be = u16; -using u32_be = u32; -using u64_be = u64; - -using s16_be = s16; -using s32_be = s32; -using s64_be = s64; - -template -using enum_be = std::enable_if_t, T>; - -using float_be = float; -using double_be = double; +using BETag = KeepTag; +using LETag = SwapTag; #endif + +// Aliases for LE types +using u16_le = AddEndian::type; +using u32_le = AddEndian::type; +using u64_le = AddEndian::type; + +using s16_le = AddEndian::type; +using s32_le = AddEndian::type; +using s64_le = AddEndian::type; + +template +using enum_le = std::enable_if_t, typename AddEndian::type>; + +using float_le = AddEndian::type; +using double_le = AddEndian::type; + +// Aliases for BE types +using u16_be = AddEndian::type; +using u32_be = AddEndian::type; +using u64_be = AddEndian::type; + +using s16_be = AddEndian::type; +using s32_be = AddEndian::type; +using s64_be = AddEndian::type; + +template +using enum_be = std::enable_if_t, typename AddEndian::type>; + +using float_be = AddEndian::type; +using double_be = AddEndian::type; diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h index 79f87a6ef..89a2d1289 100644 --- a/src/core/file_sys/ncch_container.h +++ b/src/core/file_sys/ncch_container.h @@ -129,18 +129,18 @@ struct ExHeader_StorageInfo { u64_le ext_save_data_id; // When using extended savedata access // Prefer the ID specified in the most significant bits - BitField<40, 20, u64_le> extdata_id3; - BitField<20, 20, u64_le> extdata_id4; - BitField<0, 20, u64_le> extdata_id5; + BitField<40, 20, u64> extdata_id3; + BitField<20, 20, u64> extdata_id4; + BitField<0, 20, u64> extdata_id5; }; u8 system_save_data_id[8]; union { u64_le storage_accessible_unique_ids; // When using extended savedata access // Prefer the ID specified in the most significant bits - BitField<40, 20, u64_le> extdata_id0; - BitField<20, 20, u64_le> extdata_id1; - BitField<0, 20, u64_le> extdata_id2; + BitField<40, 20, u64> extdata_id0; + BitField<20, 20, u64> extdata_id1; + BitField<0, 20, u64> extdata_id2; }; u8 access_info[7]; u8 other_attributes; diff --git a/src/core/hle/service/ir/extra_hid.h b/src/core/hle/service/ir/extra_hid.h index f9c2f050b..3c6b388e4 100644 --- a/src/core/hle/service/ir/extra_hid.h +++ b/src/core/hle/service/ir/extra_hid.h @@ -19,9 +19,9 @@ namespace Service::IR { struct ExtraHIDResponse { union { - BitField<0, 8, u32_le> header; - BitField<8, 12, u32_le> c_stick_x; - BitField<20, 12, u32_le> c_stick_y; + BitField<0, 8, u32> header; + BitField<8, 12, u32> c_stick_x; + BitField<20, 12, u32> c_stick_y; } c_stick; union { BitField<0, 5, u8> battery_level; diff --git a/src/core/hle/service/ir/ir_rst.h b/src/core/hle/service/ir/ir_rst.h index e72adae7c..df7f768e4 100644 --- a/src/core/hle/service/ir/ir_rst.h +++ b/src/core/hle/service/ir/ir_rst.h @@ -27,13 +27,13 @@ namespace Service::IR { union PadState { u32_le hex{}; - BitField<14, 1, u32_le> zl; - BitField<15, 1, u32_le> zr; + BitField<14, 1, u32> zl; + BitField<15, 1, u32> zr; - BitField<24, 1, u32_le> c_stick_right; - BitField<25, 1, u32_le> c_stick_left; - BitField<26, 1, u32_le> c_stick_up; - BitField<27, 1, u32_le> c_stick_down; + BitField<24, 1, u32> c_stick_right; + BitField<25, 1, u32> c_stick_left; + BitField<26, 1, u32> c_stick_up; + BitField<27, 1, u32> c_stick_down; }; /// Interface to "ir:rst" service diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h index 1eee90045..c9b1c7993 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.h +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -231,8 +231,8 @@ private: */ union SegmentTag { u32_le raw; - BitField<0, 4, u32_le> segment_index; - BitField<4, 28, u32_le> offset_into_segment; + BitField<0, 4, u32> segment_index; + BitField<4, 28, u32> offset_into_segment; SegmentTag() = default; explicit SegmentTag(u32 raw_) : raw(raw_) {} @@ -270,8 +270,8 @@ private: u16_le test_bit; // bit address into the name to test union Child { u16_le raw; - BitField<0, 15, u16_le> next_index; - BitField<15, 1, u16_le> is_end; + BitField<0, 15, u16> next_index; + BitField<15, 1, u16> is_end; } left, right; u16_le export_table_index; // index of an ExportNamedSymbolEntry diff --git a/src/core/movie.cpp b/src/core/movie.cpp index 5f2835821..2d81ed7e3 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -45,20 +45,20 @@ struct ControllerState { union { u16_le hex; - BitField<0, 1, u16_le> a; - BitField<1, 1, u16_le> b; - BitField<2, 1, u16_le> select; - BitField<3, 1, u16_le> start; - BitField<4, 1, u16_le> right; - BitField<5, 1, u16_le> left; - BitField<6, 1, u16_le> up; - BitField<7, 1, u16_le> down; - BitField<8, 1, u16_le> r; - BitField<9, 1, u16_le> l; - BitField<10, 1, u16_le> x; - BitField<11, 1, u16_le> y; - BitField<12, 1, u16_le> debug; - BitField<13, 1, u16_le> gpio14; + BitField<0, 1, u16> a; + BitField<1, 1, u16> b; + BitField<2, 1, u16> select; + BitField<3, 1, u16> start; + BitField<4, 1, u16> right; + BitField<5, 1, u16> left; + BitField<6, 1, u16> up; + BitField<7, 1, u16> down; + BitField<8, 1, u16> r; + BitField<9, 1, u16> l; + BitField<10, 1, u16> x; + BitField<11, 1, u16> y; + BitField<12, 1, u16> debug; + BitField<13, 1, u16> gpio14; // Bits 14-15 are currently unused }; s16_le circle_pad_x; @@ -96,12 +96,12 @@ struct ControllerState { union { u32_le hex; - BitField<0, 5, u32_le> battery_level; - BitField<5, 1, u32_le> zl_not_held; - BitField<6, 1, u32_le> zr_not_held; - BitField<7, 1, u32_le> r_not_held; - BitField<8, 12, u32_le> c_stick_x; - BitField<20, 12, u32_le> c_stick_y; + BitField<0, 5, u32> battery_level; + BitField<5, 1, u32> zl_not_held; + BitField<6, 1, u32> zr_not_held; + BitField<7, 1, u32> r_not_held; + BitField<8, 12, u32> c_stick_x; + BitField<20, 12, u32> c_stick_y; }; } extra_hid_response; }; diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index 1274b33b3..d31bbeb89 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h @@ -141,22 +141,22 @@ struct PadData { // The following union isn't trivially copyable but we don't use this input anyway. // union DigitalButton { // u16_le button; - // BitField<0, 1, u16_le> button_1; // Share - // BitField<1, 1, u16_le> button_2; // L3 - // BitField<2, 1, u16_le> button_3; // R3 - // BitField<3, 1, u16_le> button_4; // Options - // BitField<4, 1, u16_le> button_5; // Up - // BitField<5, 1, u16_le> button_6; // Right - // BitField<6, 1, u16_le> button_7; // Down - // BitField<7, 1, u16_le> button_8; // Left - // BitField<8, 1, u16_le> button_9; // L2 - // BitField<9, 1, u16_le> button_10; // R2 - // BitField<10, 1, u16_le> button_11; // L1 - // BitField<11, 1, u16_le> button_12; // R1 - // BitField<12, 1, u16_le> button_13; // Triangle - // BitField<13, 1, u16_le> button_14; // Circle - // BitField<14, 1, u16_le> button_15; // Cross - // BitField<15, 1, u16_le> button_16; // Square + // BitField<0, 1, u16> button_1; // Share + // BitField<1, 1, u16> button_2; // L3 + // BitField<2, 1, u16> button_3; // R3 + // BitField<3, 1, u16> button_4; // Options + // BitField<4, 1, u16> button_5; // Up + // BitField<5, 1, u16> button_6; // Right + // BitField<6, 1, u16> button_7; // Down + // BitField<7, 1, u16> button_8; // Left + // BitField<8, 1, u16> button_9; // L2 + // BitField<9, 1, u16> button_10; // R2 + // BitField<10, 1, u16> button_11; // L1 + // BitField<11, 1, u16> button_12; // R1 + // BitField<12, 1, u16> button_13; // Triangle + // BitField<13, 1, u16> button_14; // Circle + // BitField<14, 1, u16> button_15; // Cross + // BitField<15, 1, u16> button_16; // Square // } digital_button; u8 home; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index f048714a7..0cb36a2ce 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(tests + common/bit_field.cpp common/param_package.cpp core/arm/arm_test_common.cpp core/arm/arm_test_common.h diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp new file mode 100644 index 000000000..8ca1889f9 --- /dev/null +++ b/src/tests/common/bit_field.cpp @@ -0,0 +1,90 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include "common/bit_field.h" + +TEST_CASE("BitField", "[common]") { + enum class TestEnum : u32 { + A = 0b10111101, + B = 0b10101110, + C = 0b00001111, + }; + + union LEBitField { + u32_le raw; + BitField<0, 6, u32> a; + BitField<6, 4, s32> b; + BitField<10, 8, TestEnum> c; + BitField<18, 14, u32> d; + } le_bitfield; + + union BEBitField { + u32_be raw; + BitFieldBE<0, 6, u32> a; + BitFieldBE<6, 4, s32> b; + BitFieldBE<10, 8, TestEnum> c; + BitFieldBE<18, 14, u32> d; + } be_bitfield; + + static_assert(sizeof(LEBitField) == sizeof(u32)); + static_assert(sizeof(BEBitField) == sizeof(u32)); + static_assert(std::is_trivially_copyable_v); + static_assert(std::is_trivially_copyable_v); + + std::array raw{{ + 0b01101100, + 0b11110110, + 0b10111010, + 0b11101100, + }}; + + std::memcpy(&le_bitfield, &raw, sizeof(raw)); + std::memcpy(&be_bitfield, &raw, sizeof(raw)); + + // bit fields: 11101100101110'10111101'1001'101100 + REQUIRE(le_bitfield.raw == 0b11101100'10111010'11110110'01101100); + REQUIRE(le_bitfield.a == 0b101100); + REQUIRE(le_bitfield.b == -7); // 1001 as two's complement + REQUIRE(le_bitfield.c == TestEnum::A); + REQUIRE(le_bitfield.d == 0b11101100101110); + + le_bitfield.a.Assign(0b000111); + le_bitfield.b.Assign(-1); + le_bitfield.c.Assign(TestEnum::C); + le_bitfield.d.Assign(0b01010101010101); + std::memcpy(&raw, &le_bitfield, sizeof(raw)); + // bit fields: 01010101010101'00001111'1111'000111 + REQUIRE(le_bitfield.raw == 0b01010101'01010100'00111111'11000111); + REQUIRE(raw == std::array{{ + 0b11000111, + 0b00111111, + 0b01010100, + 0b01010101, + }}); + + // bit fields: 01101100111101'10101110'1011'101100 + REQUIRE(be_bitfield.raw == 0b01101100'11110110'10111010'11101100); + REQUIRE(be_bitfield.a == 0b101100); + REQUIRE(be_bitfield.b == -5); // 1011 as two's complement + REQUIRE(be_bitfield.c == TestEnum::B); + REQUIRE(be_bitfield.d == 0b01101100111101); + + be_bitfield.a.Assign(0b000111); + be_bitfield.b.Assign(-1); + be_bitfield.c.Assign(TestEnum::C); + be_bitfield.d.Assign(0b01010101010101); + std::memcpy(&raw, &be_bitfield, sizeof(raw)); + // bit fields: 01010101010101'00001111'1111'000111 + REQUIRE(be_bitfield.raw == 0b01010101'01010100'00111111'11000111); + REQUIRE(raw == std::array{{ + 0b01010101, + 0b01010100, + 0b00111111, + 0b11000111, + }}); +}