summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/bit_field.h12
-rw-r--r--src/common/common_types.h7
-rw-r--r--src/common/memory_hook.cpp11
-rw-r--r--src/common/memory_hook.h47
-rw-r--r--src/common/page_table.cpp31
-rw-r--r--src/common/page_table.h84
-rw-r--r--src/common/swap.h174
-rw-r--r--src/common/thread_queue_list.h6
-rw-r--r--src/common/uint128.cpp45
-rw-r--r--src/common/uint128.h19
11 files changed, 397 insertions, 45 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 3d30f0e3e..43ae8a9e7 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -92,10 +92,14 @@ add_library(common STATIC
logging/text_formatter.cpp
logging/text_formatter.h
math_util.h
+ memory_hook.cpp
+ memory_hook.h
microprofile.cpp
microprofile.h
microprofileui.h
misc.cpp
+ page_table.cpp
+ page_table.h
param_package.cpp
param_package.h
quaternion.h
@@ -114,6 +118,8 @@ add_library(common STATIC
threadsafe_queue.h
timer.cpp
timer.h
+ uint128.cpp
+ uint128.h
vector_math.h
web_result.h
)
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 7433c39ba..902e668e3 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -34,6 +34,7 @@
#include <limits>
#include <type_traits>
#include "common/common_funcs.h"
+#include "common/swap.h"
/*
* Abstract bitfield class
@@ -108,7 +109,7 @@
* symptoms.
*/
#pragma pack(1)
-template <std::size_t Position, std::size_t Bits, typename T>
+template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag>
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<UnderlyingType>;
+ using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type;
+
public:
/// Constants to allow limited introspection of fields if needed
static constexpr std::size_t position = Position;
@@ -170,7 +173,7 @@ public:
}
constexpr FORCE_INLINE void Assign(const T& value) {
- storage = (storage & ~mask) | FormatValue(value);
+ storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
}
constexpr T Value() const {
@@ -182,7 +185,7 @@ public:
}
private:
- StorageType storage;
+ StorageTypeWithEndian storage;
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
@@ -193,3 +196,6 @@ private:
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField");
};
#pragma pack()
+
+template <std::size_t Position, std::size_t Bits, typename T>
+using BitFieldBE = BitField<Position, Bits, T, BETag>;
diff --git a/src/common/common_types.h b/src/common/common_types.h
index 6b1766dca..4cec89fbd 100644
--- a/src/common/common_types.h
+++ b/src/common/common_types.h
@@ -40,10 +40,9 @@ using s64 = std::int64_t; ///< 64-bit signed int
using f32 = float; ///< 32-bit floating point
using f64 = double; ///< 64-bit floating point
-// TODO: It would be nice to eventually replace these with strong types that prevent accidental
-// conversion between each other.
-using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
-using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
+using VAddr = u64; ///< Represents a pointer in the userspace virtual address space.
+using PAddr = u64; ///< Represents a pointer in the ARM11 physical address space.
+using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space.
using u128 = std::array<std::uint64_t, 2>;
static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
diff --git a/src/common/memory_hook.cpp b/src/common/memory_hook.cpp
new file mode 100644
index 000000000..3986986d6
--- /dev/null
+++ b/src/common/memory_hook.cpp
@@ -0,0 +1,11 @@
+// Copyright 2018 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/memory_hook.h"
+
+namespace Common {
+
+MemoryHook::~MemoryHook() = default;
+
+} // namespace Common
diff --git a/src/common/memory_hook.h b/src/common/memory_hook.h
new file mode 100644
index 000000000..adaa4c2c5
--- /dev/null
+++ b/src/common/memory_hook.h
@@ -0,0 +1,47 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <optional>
+
+#include "common/common_types.h"
+
+namespace Common {
+
+/**
+ * Memory hooks have two purposes:
+ * 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement
+ * texture forwarding and memory breakpoints for debugging.
+ * 2. To allow for the implementation of MMIO devices.
+ *
+ * A hook may be mapped to multiple regions of memory.
+ *
+ * If a std::nullopt or false is returned from a function, the read/write request is passed through
+ * to the underlying memory region.
+ */
+class MemoryHook {
+public:
+ virtual ~MemoryHook();
+
+ virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
+
+ virtual std::optional<u8> Read8(VAddr addr) = 0;
+ virtual std::optional<u16> Read16(VAddr addr) = 0;
+ virtual std::optional<u32> Read32(VAddr addr) = 0;
+ virtual std::optional<u64> Read64(VAddr addr) = 0;
+
+ virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
+
+ virtual bool Write8(VAddr addr, u8 data) = 0;
+ virtual bool Write16(VAddr addr, u16 data) = 0;
+ virtual bool Write32(VAddr addr, u32 data) = 0;
+ virtual bool Write64(VAddr addr, u64 data) = 0;
+
+ virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
+};
+
+using MemoryHookPointer = std::shared_ptr<MemoryHook>;
+} // namespace Common
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
new file mode 100644
index 000000000..69b7abc54
--- /dev/null
+++ b/src/common/page_table.cpp
@@ -0,0 +1,31 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/page_table.h"
+
+namespace Common {
+
+PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {}
+
+PageTable::~PageTable() = default;
+
+void PageTable::Resize(std::size_t address_space_width_in_bits) {
+ const std::size_t num_page_table_entries = 1ULL
+ << (address_space_width_in_bits - page_size_in_bits);
+
+ pointers.resize(num_page_table_entries);
+ attributes.resize(num_page_table_entries);
+ backing_addr.resize(num_page_table_entries);
+
+ // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
+ // vector size is subsequently decreased (via resize), the vector might not automatically
+ // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
+ // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
+
+ pointers.shrink_to_fit();
+ attributes.shrink_to_fit();
+ backing_addr.shrink_to_fit();
+}
+
+} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
new file mode 100644
index 000000000..8b8ff0bb8
--- /dev/null
+++ b/src/common/page_table.h
@@ -0,0 +1,84 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include <boost/icl/interval_map.hpp>
+#include "common/common_types.h"
+#include "common/memory_hook.h"
+
+namespace Common {
+
+enum class PageType : u8 {
+ /// Page is unmapped and should cause an access error.
+ Unmapped,
+ /// Page is mapped to regular memory. This is the only type you can get pointers to.
+ Memory,
+ /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
+ /// invalidation
+ RasterizerCachedMemory,
+ /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
+ Special,
+ /// Page is allocated for use.
+ Allocated,
+};
+
+struct SpecialRegion {
+ enum class Type {
+ DebugHook,
+ IODevice,
+ } type;
+
+ MemoryHookPointer handler;
+
+ bool operator<(const SpecialRegion& other) const {
+ return std::tie(type, handler) < std::tie(other.type, other.handler);
+ }
+
+ bool operator==(const SpecialRegion& other) const {
+ return std::tie(type, handler) == std::tie(other.type, other.handler);
+ }
+};
+
+/**
+ * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
+ * mimics the way a real CPU page table works.
+ */
+struct PageTable {
+ explicit PageTable(std::size_t page_size_in_bits);
+ ~PageTable();
+
+ /**
+ * Resizes the page table to be able to accomodate enough pages within
+ * a given address space.
+ *
+ * @param address_space_width_in_bits The address size width in bits.
+ */
+ void Resize(std::size_t address_space_width_in_bits);
+
+ /**
+ * Vector of memory pointers backing each page. An entry can only be non-null if the
+ * corresponding entry in the `attributes` vector is of type `Memory`.
+ */
+ std::vector<u8*> pointers;
+
+ /**
+ * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
+ * of type `Special`.
+ */
+ boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions;
+
+ /**
+ * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
+ * the corresponding entry in `pointers` MUST be set to null.
+ */
+ std::vector<PageType> attributes;
+
+ std::vector<u64> backing_addr;
+
+ const std::size_t page_size_in_bits{};
+};
+
+} // namespace Common
diff --git a/src/common/swap.h b/src/common/swap.h
index 0e219747f..b3eab1324 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -17,6 +17,8 @@
#pragma once
+#include <type_traits>
+
#if defined(_MSC_VER)
#include <cstdlib>
#elif defined(__linux__)
@@ -170,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);
@@ -605,52 +607,154 @@ struct swap_double_t {
}
};
-#if COMMON_LITTLE_ENDIAN
-using u16_le = u16;
-using u32_le = u32;
-using u64_le = u64;
+template <typename T>
+struct swap_enum_t {
+ static_assert(std::is_enum_v<T>);
+ using base = std::underlying_type_t<T>;
+
+public:
+ swap_enum_t() = default;
+ swap_enum_t(const T& v) : value(swap(v)) {}
+
+ swap_enum_t& operator=(const T& v) {
+ value = swap(v);
+ return *this;
+ }
+
+ operator T() const {
+ return swap(value);
+ }
+
+ explicit operator base() const {
+ return static_cast<base>(swap(value));
+ }
-using s16_le = s16;
-using s32_le = s32;
-using s64_le = s64;
+protected:
+ T value{};
+ // clang-format off
+ using swap_t = std::conditional_t<
+ std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
+ std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
+ std::is_same_v<base, u32>, swap_32_t<u32>, std::conditional_t<
+ std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
+ std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
+ std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
+ // clang-format on
+ static T swap(T x) {
+ return static_cast<T>(swap_t::swap(static_cast<base>(x)));
+ }
+};
-using float_le = float;
-using double_le = double;
+struct SwapTag {}; // Use the different endianness from the system
+struct KeepTag {}; // Use the same endianness as the system
-using u64_be = swap_struct_t<u64, swap_64_t<u64>>;
-using s64_be = swap_struct_t<s64, swap_64_t<s64>>;
+template <typename T, typename Tag>
+struct AddEndian;
-using u32_be = swap_struct_t<u32, swap_32_t<u32>>;
-using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
+// KeepTag specializations
-using u16_be = swap_struct_t<u16, swap_16_t<u16>>;
-using s16_be = swap_struct_t<s16, swap_16_t<s16>>;
+template <typename T>
+struct AddEndian<T, KeepTag> {
+ using type = T;
+};
-using float_be = swap_struct_t<float, swap_float_t<float>>;
-using double_be = swap_struct_t<double, swap_double_t<double>>;
-#else
+// SwapTag specializations
+
+template <>
+struct AddEndian<u8, SwapTag> {
+ using type = u8;
+};
+
+template <>
+struct AddEndian<u16, SwapTag> {
+ using type = swap_struct_t<u16, swap_16_t<u16>>;
+};
+
+template <>
+struct AddEndian<u32, SwapTag> {
+ using type = swap_struct_t<u32, swap_32_t<u32>>;
+};
-using u64_le = swap_struct_t<u64, swap_64_t<u64>>;
-using s64_le = swap_struct_t<s64, swap_64_t<s64>>;
+template <>
+struct AddEndian<u64, SwapTag> {
+ using type = swap_struct_t<u64, swap_64_t<u64>>;
+};
+
+template <>
+struct AddEndian<s8, SwapTag> {
+ using type = s8;
+};
-using u32_le = swap_struct_t<u32, swap_32_t<u32>>;
-using s32_le = swap_struct_t<s32, swap_32_t<s32>>;
+template <>
+struct AddEndian<s16, SwapTag> {
+ using type = swap_struct_t<s16, swap_16_t<s16>>;
+};
-using u16_le = swap_struct_t<u16, swap_16_t<u16>>;
-using s16_le = swap_struct_t<s16, swap_16_t<s16>>;
+template <>
+struct AddEndian<s32, SwapTag> {
+ using type = swap_struct_t<s32, swap_32_t<s32>>;
+};
+
+template <>
+struct AddEndian<s64, SwapTag> {
+ using type = swap_struct_t<s64, swap_64_t<s64>>;
+};
+
+template <>
+struct AddEndian<float, SwapTag> {
+ using type = swap_struct_t<float, swap_float_t<float>>;
+};
+
+template <>
+struct AddEndian<double, SwapTag> {
+ using type = swap_struct_t<double, swap_double_t<double>>;
+};
+
+template <typename T>
+struct AddEndian<T, SwapTag> {
+ static_assert(std::is_enum_v<T>);
+ using type = swap_enum_t<T>;
+};
-using float_le = swap_struct_t<float, swap_float_t<float>>;
-using double_le = swap_struct_t<double, swap_double_t<double>>;
+// Alias LETag/BETag as KeepTag/SwapTag depending on the system
+#if COMMON_LITTLE_ENDIAN
-using u16_be = u16;
-using u32_be = u32;
-using u64_be = u64;
+using LETag = KeepTag;
+using BETag = SwapTag;
-using s16_be = s16;
-using s32_be = s32;
-using s64_be = s64;
+#else
-using float_be = float;
-using double_be = double;
+using BETag = KeepTag;
+using LETag = SwapTag;
#endif
+
+// Aliases for LE types
+using u16_le = AddEndian<u16, LETag>::type;
+using u32_le = AddEndian<u32, LETag>::type;
+using u64_le = AddEndian<u64, LETag>::type;
+
+using s16_le = AddEndian<s16, LETag>::type;
+using s32_le = AddEndian<s32, LETag>::type;
+using s64_le = AddEndian<s64, LETag>::type;
+
+template <typename T>
+using enum_le = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, LETag>::type>;
+
+using float_le = AddEndian<float, LETag>::type;
+using double_le = AddEndian<double, LETag>::type;
+
+// Aliases for BE types
+using u16_be = AddEndian<u16, BETag>::type;
+using u32_be = AddEndian<u32, BETag>::type;
+using u64_be = AddEndian<u64, BETag>::type;
+
+using s16_be = AddEndian<s16, BETag>::type;
+using s32_be = AddEndian<s32, BETag>::type;
+using s64_be = AddEndian<s64, BETag>::type;
+
+template <typename T>
+using enum_be = std::enable_if_t<std::is_enum_v<T>, typename AddEndian<T, BETag>::type>;
+
+using float_be = AddEndian<float, BETag>::type;
+using double_be = AddEndian<double, BETag>::type;
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index e7594db68..791f99a8c 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -6,7 +6,6 @@
#include <array>
#include <deque>
-#include <boost/range/algorithm_ext/erase.hpp>
namespace Common {
@@ -111,8 +110,9 @@ struct ThreadQueueList {
}
void remove(Priority priority, const T& thread_id) {
- Queue* cur = &queues[priority];
- boost::remove_erase(cur->data, thread_id);
+ Queue* const cur = &queues[priority];
+ const auto iter = std::remove(cur->data.begin(), cur->data.end(), thread_id);
+ cur->data.erase(iter, cur->data.end());
}
void rotate(Priority priority) {
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp
new file mode 100644
index 000000000..32bf56730
--- /dev/null
+++ b/src/common/uint128.cpp
@@ -0,0 +1,45 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#ifdef _MSC_VER
+#include <intrin.h>
+
+#pragma intrinsic(_umul128)
+#endif
+#include <cstring>
+#include "common/uint128.h"
+
+namespace Common {
+
+u128 Multiply64Into128(u64 a, u64 b) {
+ u128 result;
+#ifdef _MSC_VER
+ result[0] = _umul128(a, b, &result[1]);
+#else
+ unsigned __int128 tmp = a;
+ tmp *= b;
+ std::memcpy(&result, &tmp, sizeof(u128));
+#endif
+ return result;
+}
+
+std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
+ u64 remainder = dividend[0] % divisor;
+ u64 accum = dividend[0] / divisor;
+ if (dividend[1] == 0)
+ return {accum, remainder};
+ // We ignore dividend[1] / divisor as that overflows
+ const u64 first_segment = (dividend[1] % divisor) << 32;
+ accum += (first_segment / divisor) << 32;
+ const u64 second_segment = (first_segment % divisor) << 32;
+ accum += (second_segment / divisor);
+ remainder += second_segment % divisor;
+ if (remainder >= divisor) {
+ accum++;
+ remainder -= divisor;
+ }
+ return {accum, remainder};
+}
+
+} // namespace Common
diff --git a/src/common/uint128.h b/src/common/uint128.h
new file mode 100644
index 000000000..a3be2a2cb
--- /dev/null
+++ b/src/common/uint128.h
@@ -0,0 +1,19 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <utility>
+#include "common/common_types.h"
+
+namespace Common {
+
+// This function multiplies 2 u64 values and produces a u128 value;
+u128 Multiply64Into128(u64 a, u64 b);
+
+// This function divides a u128 by a u32 value and produces two u64 values:
+// the result of division and the remainder
+std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
+
+} // namespace Common