mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-03 21:41:26 +12:00
New bitsets (experimental)
This commit is contained in:
parent
71441819e5
commit
46735d6b3d
20 changed files with 802 additions and 361 deletions
|
@ -1,269 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
// Small bitset for enum class types with available values [0, BitSize).
|
|
||||||
// T must be either enum type or convertible to (registered with via simple_t<T>).
|
|
||||||
// Internal representation is single value of type T.
|
|
||||||
template<typename T, std::size_t BitSize = sizeof(T) * CHAR_BIT>
|
|
||||||
struct bitset_t
|
|
||||||
{
|
|
||||||
using type = simple_t<T>;
|
|
||||||
using under = std::underlying_type_t<type>;
|
|
||||||
enum class raw_type : under {};
|
|
||||||
|
|
||||||
static constexpr auto bitsize = BitSize;
|
|
||||||
|
|
||||||
bitset_t() = default;
|
|
||||||
|
|
||||||
constexpr bitset_t(type _enum_const)
|
|
||||||
: m_value(static_cast<type>(shift(_enum_const)))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bitset_t(raw_type raw_value)
|
|
||||||
: m_value(static_cast<T>(static_cast<under>(raw_value)))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get underlying value
|
|
||||||
constexpr under _value() const
|
|
||||||
{
|
|
||||||
return static_cast<under>(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit constexpr operator bool() const
|
|
||||||
{
|
|
||||||
return _value() ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitset_t& operator +=(bitset_t rhs)
|
|
||||||
{
|
|
||||||
return *this = static_cast<raw_type>(_value() | rhs._value());
|
|
||||||
}
|
|
||||||
|
|
||||||
bitset_t& operator -=(bitset_t rhs)
|
|
||||||
{
|
|
||||||
return *this = static_cast<raw_type>(_value() & ~rhs._value());
|
|
||||||
}
|
|
||||||
|
|
||||||
bitset_t& operator &=(bitset_t rhs)
|
|
||||||
{
|
|
||||||
return *this = static_cast<raw_type>(_value() & rhs._value());
|
|
||||||
}
|
|
||||||
|
|
||||||
bitset_t& operator ^=(bitset_t rhs)
|
|
||||||
{
|
|
||||||
return *this = static_cast<raw_type>(_value() ^ rhs._value());
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bitset_t operator +(bitset_t lhs, bitset_t rhs)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(lhs._value() | rhs._value());
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bitset_t operator -(bitset_t lhs, bitset_t rhs)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(lhs._value() & ~rhs._value());
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bitset_t operator &(bitset_t lhs, bitset_t rhs)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(lhs._value() & rhs._value());
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bitset_t operator ^(bitset_t lhs, bitset_t rhs)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(lhs._value() ^ rhs._value());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool test(bitset_t rhs) const
|
|
||||||
{
|
|
||||||
const under v = _value();
|
|
||||||
const under s = rhs._value();
|
|
||||||
return (v & s) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool test_and_set(bitset_t rhs)
|
|
||||||
{
|
|
||||||
const under v = _value();
|
|
||||||
const under s = rhs._value();
|
|
||||||
*this = static_cast<raw_type>(v | s);
|
|
||||||
return (v & s) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool test_and_reset(bitset_t rhs)
|
|
||||||
{
|
|
||||||
const under v = _value();
|
|
||||||
const under s = rhs._value();
|
|
||||||
*this = static_cast<raw_type>(v & ~s);
|
|
||||||
return (v & s) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool test_and_complement(bitset_t rhs)
|
|
||||||
{
|
|
||||||
const under v = _value();
|
|
||||||
const under s = rhs._value();
|
|
||||||
*this = static_cast<raw_type>(v ^ s);
|
|
||||||
return (v & s) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr under shift(const T& value)
|
|
||||||
{
|
|
||||||
return static_cast<under>(value) < BitSize ? static_cast<under>(1) << static_cast<under>(value) : throw value;
|
|
||||||
}
|
|
||||||
|
|
||||||
T m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename RT = T>
|
|
||||||
constexpr RT make_bitset()
|
|
||||||
{
|
|
||||||
return RT{};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fold enum constants into bitset_t<> (must be implemented with constexpr initializer_list constructor instead)
|
|
||||||
template<typename T = void, typename Arg, typename... Args, typename RT = std::conditional_t<std::is_void<T>::value, bitset_t<Arg>, T>>
|
|
||||||
constexpr RT make_bitset(Arg&& _enum_const, Args&&... args)
|
|
||||||
{
|
|
||||||
return RT{ std::forward<Arg>(_enum_const) } + make_bitset<RT>(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename CT>
|
|
||||||
struct atomic_add<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
|
||||||
{
|
|
||||||
using under = typename bitset_t<T>::under;
|
|
||||||
using raw_type = typename bitset_t<T>::raw_type;
|
|
||||||
|
|
||||||
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), right._value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto fetch_op = &op1;
|
|
||||||
|
|
||||||
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), right._value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto op_fetch = &op2;
|
|
||||||
static constexpr auto atomic_op = &op2;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename CT>
|
|
||||||
struct atomic_sub<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
|
||||||
{
|
|
||||||
using under = typename bitset_t<T>::under;
|
|
||||||
using raw_type = typename bitset_t<T>::raw_type;
|
|
||||||
|
|
||||||
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~right._value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto fetch_op = &op1;
|
|
||||||
|
|
||||||
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~right._value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto op_fetch = &op2;
|
|
||||||
static constexpr auto atomic_op = &op2;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename CT>
|
|
||||||
struct atomic_and<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
|
||||||
{
|
|
||||||
using under = typename bitset_t<T>::under;
|
|
||||||
using raw_type = typename bitset_t<T>::raw_type;
|
|
||||||
|
|
||||||
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), right._value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto fetch_op = &op1;
|
|
||||||
|
|
||||||
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), right._value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto op_fetch = &op2;
|
|
||||||
static constexpr auto atomic_op = &op2;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename CT>
|
|
||||||
struct atomic_xor<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
|
||||||
{
|
|
||||||
using under = typename bitset_t<T>::under;
|
|
||||||
using raw_type = typename bitset_t<T>::raw_type;
|
|
||||||
|
|
||||||
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), right._value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto fetch_op = &op1;
|
|
||||||
|
|
||||||
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
|
|
||||||
{
|
|
||||||
return static_cast<raw_type>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), right._value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto op_fetch = &op2;
|
|
||||||
static constexpr auto atomic_op = &op2;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct atomic_test_and_set<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
|
||||||
{
|
|
||||||
using under = typename bitset_t<T>::under;
|
|
||||||
|
|
||||||
static inline bool _op(bitset_t<T>& left, const T& value)
|
|
||||||
{
|
|
||||||
return atomic_storage<under>::bts(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto atomic_op = &_op;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct atomic_test_and_reset<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
|
||||||
{
|
|
||||||
using under = typename bitset_t<T>::under;
|
|
||||||
|
|
||||||
static inline bool _op(bitset_t<T>& left, const T& value)
|
|
||||||
{
|
|
||||||
return atomic_storage<under>::btr(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto atomic_op = &_op;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct atomic_test_and_complement<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
|
||||||
{
|
|
||||||
using under = typename bitset_t<T>::under;
|
|
||||||
|
|
||||||
static inline bool _op(bitset_t<T>& left, const T& value)
|
|
||||||
{
|
|
||||||
return atomic_storage<under>::btc(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto atomic_op = &_op;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, std::size_t BitSize>
|
|
||||||
struct fmt_unveil<bitset_t<T, BitSize>, void>
|
|
||||||
{
|
|
||||||
using type = typename bitset_t<T, BitSize>::raw_type;
|
|
||||||
|
|
||||||
static inline auto get(const bitset_t<T, BitSize>& value)
|
|
||||||
{
|
|
||||||
return fmt_unveil<type>::get(static_cast<type>(value._value()));
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -630,7 +630,7 @@ void fs::file::xfail() const
|
||||||
throw fmt::exception("Unexpected fs::error %s", g_tls_error);
|
throw fmt::exception("Unexpected fs::error %s", g_tls_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs::file::open(const std::string& path, bitset_t<open_mode> mode)
|
bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
|
||||||
{
|
{
|
||||||
if (auto device = get_virtual_device(path))
|
if (auto device = get_virtual_device(path))
|
||||||
{
|
{
|
||||||
|
@ -645,25 +645,25 @@ bool fs::file::open(const std::string& path, bitset_t<open_mode> mode)
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
DWORD access = 0;
|
DWORD access = 0;
|
||||||
if (mode & fs::read) access |= GENERIC_READ;
|
if (test(mode & fs::read)) access |= GENERIC_READ;
|
||||||
if (mode & fs::write) access |= mode & fs::append ? FILE_APPEND_DATA : GENERIC_WRITE;
|
if (test(mode & fs::write)) access |= test(mode & fs::append) ? FILE_APPEND_DATA : GENERIC_WRITE;
|
||||||
|
|
||||||
DWORD disp = 0;
|
DWORD disp = 0;
|
||||||
if (mode & fs::create)
|
if (test(mode & fs::create))
|
||||||
{
|
{
|
||||||
disp =
|
disp =
|
||||||
mode & fs::excl ? CREATE_NEW :
|
test(mode & fs::excl) ? CREATE_NEW :
|
||||||
mode & fs::trunc ? CREATE_ALWAYS : OPEN_ALWAYS;
|
test(mode & fs::trunc) ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mode & fs::excl)
|
if (test(mode & fs::excl))
|
||||||
{
|
{
|
||||||
g_tls_error = error::inval;
|
g_tls_error = error::inval;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
disp = mode & fs::trunc ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HANDLE handle = CreateFileW(to_wchar(path).get(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
|
const HANDLE handle = CreateFileW(to_wchar(path).get(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
@ -801,14 +801,14 @@ bool fs::file::open(const std::string& path, bitset_t<open_mode> mode)
|
||||||
#else
|
#else
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
|
|
||||||
if (mode & fs::read && mode & fs::write) flags |= O_RDWR;
|
if (test(mode & fs::read) && test(mode & fs::write)) flags |= O_RDWR;
|
||||||
else if (mode & fs::read) flags |= O_RDONLY;
|
else if (test(mode & fs::read)) flags |= O_RDONLY;
|
||||||
else if (mode & fs::write) flags |= O_WRONLY;
|
else if (test(mode & fs::write)) flags |= O_WRONLY;
|
||||||
|
|
||||||
if (mode & fs::append) flags |= O_APPEND;
|
if (test(mode & fs::append)) flags |= O_APPEND;
|
||||||
if (mode & fs::create) flags |= O_CREAT;
|
if (test(mode & fs::create)) flags |= O_CREAT;
|
||||||
if (mode & fs::trunc) flags |= O_TRUNC;
|
if (test(mode & fs::trunc)) flags |= O_TRUNC;
|
||||||
if (mode & fs::excl) flags |= O_EXCL;
|
if (test(mode & fs::excl)) flags |= O_EXCL;
|
||||||
|
|
||||||
const int fd = ::open(path.c_str(), flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
const int fd = ::open(path.c_str(), flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "BitSet.h"
|
#include "bit_set.h"
|
||||||
|
|
||||||
namespace fs
|
namespace fs
|
||||||
{
|
{
|
||||||
// File open mode flags
|
// File open mode flags
|
||||||
enum struct open_mode : u32
|
enum class open_mode : u32
|
||||||
{
|
{
|
||||||
read,
|
read,
|
||||||
write,
|
write,
|
||||||
|
@ -19,16 +19,18 @@ namespace fs
|
||||||
create,
|
create,
|
||||||
trunc,
|
trunc,
|
||||||
excl,
|
excl,
|
||||||
|
|
||||||
|
__bitset_enum_max
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr bitset_t<open_mode> read = open_mode::read; // Enable reading
|
constexpr auto read = +open_mode::read; // Enable reading
|
||||||
constexpr bitset_t<open_mode> write = open_mode::write; // Enable writing
|
constexpr auto write = +open_mode::write; // Enable writing
|
||||||
constexpr bitset_t<open_mode> append = open_mode::append; // Always append to the end of the file
|
constexpr auto append = +open_mode::append; // Always append to the end of the file
|
||||||
constexpr bitset_t<open_mode> create = open_mode::create; // Create file if it doesn't exist
|
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
|
||||||
constexpr bitset_t<open_mode> trunc = open_mode::trunc; // Clear opened file if it's not empty
|
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
|
||||||
constexpr bitset_t<open_mode> excl = open_mode::excl; // Failure if the file already exists (used with `create`)
|
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
|
||||||
|
|
||||||
constexpr bitset_t<open_mode> rewrite = write + create + trunc;
|
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
||||||
|
|
||||||
// File seek mode
|
// File seek mode
|
||||||
enum class seek_mode : u32
|
enum class seek_mode : u32
|
||||||
|
@ -93,7 +95,7 @@ namespace fs
|
||||||
virtual bool remove(const std::string& path) = 0;
|
virtual bool remove(const std::string& path) = 0;
|
||||||
virtual bool trunc(const std::string& path, u64 length) = 0;
|
virtual bool trunc(const std::string& path, u64 length) = 0;
|
||||||
|
|
||||||
virtual std::unique_ptr<file_base> open(const std::string& path, bitset_t<open_mode> mode) = 0;
|
virtual std::unique_ptr<file_base> open(const std::string& path, bs_t<open_mode> mode) = 0;
|
||||||
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
|
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -151,13 +153,13 @@ namespace fs
|
||||||
file() = default;
|
file() = default;
|
||||||
|
|
||||||
// Open file with specified mode
|
// Open file with specified mode
|
||||||
explicit file(const std::string& path, bitset_t<open_mode> mode = ::fs::read)
|
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read)
|
||||||
{
|
{
|
||||||
open(path, mode);
|
open(path, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open file with specified mode
|
// Open file with specified mode
|
||||||
bool open(const std::string& path, bitset_t<open_mode> mode = ::fs::read);
|
bool open(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||||
|
|
||||||
// Open memory for read
|
// Open memory for read
|
||||||
explicit file(const void* ptr, std::size_t size);
|
explicit file(const void* ptr, std::size_t size);
|
||||||
|
|
|
@ -146,6 +146,32 @@ struct fmt_class_string
|
||||||
fmt_class_string<std::underlying_type_t<T>>::format(out, static_cast<u64>(value));
|
fmt_class_string<std::underlying_type_t<T>>::format(out, static_cast<u64>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function (bitset formatting)
|
||||||
|
static SAFE_BUFFERS FORCE_INLINE void format_bitset(std::string& out, u64 arg, const char* prefix, const char* delim, const char* suffix, void(*fmt)(std::string&, u64))
|
||||||
|
{
|
||||||
|
// Start from raw value
|
||||||
|
fmt_class_string<u64>::format(out, arg);
|
||||||
|
|
||||||
|
out += prefix;
|
||||||
|
|
||||||
|
for (u64 i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
const u64 mask = 1ull << i;
|
||||||
|
|
||||||
|
if (arg & mask)
|
||||||
|
{
|
||||||
|
fmt(out, i);
|
||||||
|
|
||||||
|
if (arg > mask)
|
||||||
|
{
|
||||||
|
out += delim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out += suffix;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper constant (may be used in format_enum as lambda return value)
|
// Helper constant (may be used in format_enum as lambda return value)
|
||||||
static constexpr const char* unknown = nullptr;
|
static constexpr const char* unknown = nullptr;
|
||||||
};
|
};
|
||||||
|
|
636
Utilities/bit_set.h
Normal file
636
Utilities/bit_set.h
Normal file
|
@ -0,0 +1,636 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
This header helps to extend scoped enum types (enum class) in two possible ways:
|
||||||
|
1) Enabling bitwise operators for enums
|
||||||
|
2) Advanced bs_t<> template (this converts enum type to another "bitset" enum type)
|
||||||
|
|
||||||
|
To enable bitwise operators, enum scope must contain `__bitwise_ops` entry.
|
||||||
|
|
||||||
|
enum class flags
|
||||||
|
{
|
||||||
|
__bitwise_ops, // Not essential, but recommended to put it first
|
||||||
|
|
||||||
|
flag1 = 1 << 0,
|
||||||
|
flag2 = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
`flags::flag1 | flags::flag2` - bitwise OR
|
||||||
|
`flags::flag1 & flags::flag2` - bitwise AND
|
||||||
|
`flags::flag1 ^ flags::flag2` - bitwise XOR
|
||||||
|
`~flags::flag1` - bitwise NEG
|
||||||
|
|
||||||
|
To enable bs_t<> template, enum scope must contain `__bitset_enum_max` entry.
|
||||||
|
|
||||||
|
enum class flagzz : u32
|
||||||
|
{
|
||||||
|
flag1, // Bit indices start from zero
|
||||||
|
flag2,
|
||||||
|
|
||||||
|
__bitset_enum_max // It must be the last value
|
||||||
|
};
|
||||||
|
|
||||||
|
Now some operators are enabled for two enum types: `flagzz` and `bs_t<flagzz>`.
|
||||||
|
These are very different from previously described bitwise operators.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
`+flagzz::flag1` - unary `+` operator convert flagzz value to bs_t<flagzz>
|
||||||
|
`flagzz::flag1 + flagzz::flag2` - bitset union
|
||||||
|
`flagzz::flag1 - flagzz::flag2` - bitset difference
|
||||||
|
Intersection (&) and symmetric difference (^) is also available.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
// Helper template
|
||||||
|
template<typename T>
|
||||||
|
struct bs_base
|
||||||
|
{
|
||||||
|
// Underlying type
|
||||||
|
using under = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
// Actual bitset type
|
||||||
|
enum class type : under
|
||||||
|
{
|
||||||
|
null = 0, // Empty bitset
|
||||||
|
|
||||||
|
__bitset_set_type = 0 // SFINAE marker
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::size_t bitmax = sizeof(T) * CHAR_BIT;
|
||||||
|
static constexpr std::size_t bitsize = static_cast<under>(T::__bitset_enum_max);
|
||||||
|
|
||||||
|
static_assert(std::is_enum<T>::value, "bs_t<> error: invalid type (must be enum)");
|
||||||
|
static_assert(!bitsize || bitsize <= bitmax, "bs_t<> error: invalid __bitset_enum_max");
|
||||||
|
|
||||||
|
// Helper function
|
||||||
|
static constexpr under shift(T value)
|
||||||
|
{
|
||||||
|
return static_cast<under>(1) << static_cast<under>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend type& operator +=(type& lhs, type rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<under&>(lhs) |= static_cast<under>(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend type& operator -=(type& lhs, type rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<under&>(lhs) &= ~static_cast<under>(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend type& operator &=(type& lhs, type rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<under&>(lhs) &= static_cast<under>(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend type& operator ^=(type& lhs, type rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<under&>(lhs) ^= static_cast<under>(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend type& operator +=(type& lhs, T rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<under&>(lhs) |= shift(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend type& operator -=(type& lhs, T rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<under&>(lhs) &= ~shift(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend type& operator &=(type& lhs, T rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<under&>(lhs) &= shift(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend type& operator ^=(type& lhs, T rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<under&>(lhs) ^= shift(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr type operator +(type lhs, type rhs)
|
||||||
|
{
|
||||||
|
return static_cast<type>(static_cast<under>(lhs) | static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr type operator -(type lhs, type rhs)
|
||||||
|
{
|
||||||
|
return static_cast<type>(static_cast<under>(lhs) & ~static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr type operator &(type lhs, type rhs)
|
||||||
|
{
|
||||||
|
return static_cast<type>(static_cast<under>(lhs) & static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr type operator ^(type lhs, type rhs)
|
||||||
|
{
|
||||||
|
return static_cast<type>(static_cast<under>(lhs) ^ static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr type operator &(type lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<type>(static_cast<under>(lhs) & shift(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr type operator ^(type lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<type>(static_cast<under>(lhs) ^ shift(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr type operator &(T lhs, type rhs)
|
||||||
|
{
|
||||||
|
return static_cast<type>(shift(lhs) & static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr type operator ^(T lhs, type rhs)
|
||||||
|
{
|
||||||
|
return static_cast<type>(shift(lhs) ^ static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator ==(T lhs, type rhs)
|
||||||
|
{
|
||||||
|
return shift(lhs) == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator ==(type lhs, T rhs)
|
||||||
|
{
|
||||||
|
return lhs == shift(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator !=(T lhs, type rhs)
|
||||||
|
{
|
||||||
|
return shift(lhs) != rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator !=(type lhs, T rhs)
|
||||||
|
{
|
||||||
|
return lhs != shift(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test(type value)
|
||||||
|
{
|
||||||
|
return static_cast<under>(value) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test(type lhs, type rhs)
|
||||||
|
{
|
||||||
|
return (static_cast<under>(lhs) & static_cast<under>(rhs)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test(type lhs, T rhs)
|
||||||
|
{
|
||||||
|
return (static_cast<under>(lhs) & shift(rhs)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test(T lhs, type rhs)
|
||||||
|
{
|
||||||
|
return (shift(lhs) & static_cast<under>(rhs)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test_and_set(type& lhs, type rhs)
|
||||||
|
{
|
||||||
|
return test_and_set(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test_and_set(type& lhs, T rhs)
|
||||||
|
{
|
||||||
|
return test_and_set(reinterpret_cast<under&>(lhs), shift(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test_and_reset(type& lhs, type rhs)
|
||||||
|
{
|
||||||
|
return test_and_reset(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test_and_reset(type& lhs, T rhs)
|
||||||
|
{
|
||||||
|
return test_and_reset(reinterpret_cast<under&>(lhs), shift(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test_and_complement(type& lhs, type rhs)
|
||||||
|
{
|
||||||
|
return test_and_complement(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool test_and_complement(type& lhs, T rhs)
|
||||||
|
{
|
||||||
|
return test_and_complement(reinterpret_cast<under&>(lhs), shift(rhs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bitset type for enum class with available bits [0, T::__bitset_enum_max)
|
||||||
|
template<typename T>
|
||||||
|
using bs_t = typename bs_base<T>::type;
|
||||||
|
|
||||||
|
// Unary '+' operator: promote plain enum value to bitset value
|
||||||
|
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||||
|
constexpr bs_t<T> operator +(T value)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(bs_base<T>::shift(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary '+' operator: bitset union
|
||||||
|
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||||
|
constexpr bs_t<T> operator +(T lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) | bs_base<T>::shift(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary '+' operator: bitset union
|
||||||
|
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||||
|
constexpr bs_t<T> operator +(typename bs_base<T>::type lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(static_cast<typename bs_base<T>::under>(lhs) | bs_base<T>::shift(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary '+' operator: bitset union
|
||||||
|
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||||
|
constexpr bs_t<T> operator +(T lhs, typename bs_base<T>::type rhs)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) | static_cast<typename bs_base<T>::under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary '-' operator: bitset difference
|
||||||
|
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||||
|
constexpr bs_t<T> operator -(T lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) & ~bs_base<T>::shift(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary '-' operator: bitset difference
|
||||||
|
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||||
|
constexpr bs_t<T> operator -(typename bs_base<T>::type lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(static_cast<typename bs_base<T>::under>(lhs) & ~bs_base<T>::shift(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary '-' operator: bitset difference
|
||||||
|
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||||
|
constexpr bs_t<T> operator -(T lhs, typename bs_base<T>::type rhs)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) & ~static_cast<typename bs_base<T>::under>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BS, typename T>
|
||||||
|
struct atomic_add<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||||
|
{
|
||||||
|
using under = typename bs_base<T>::under;
|
||||||
|
|
||||||
|
static inline bs_t<T> op1(bs_t<T>& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline bs_t<T> op2(bs_t<T>& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BS, typename T>
|
||||||
|
struct atomic_sub<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||||
|
{
|
||||||
|
using under = typename bs_base<T>::under;
|
||||||
|
|
||||||
|
static inline bs_t<T> op1(bs_t<T>& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~bs_base<T>::shift(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline bs_t<T> op2(bs_t<T>& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~bs_base<T>::shift(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BS, typename T>
|
||||||
|
struct atomic_and<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||||
|
{
|
||||||
|
using under = typename bs_base<T>::under;
|
||||||
|
|
||||||
|
static inline bs_t<T> op1(bs_t<T>& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline bs_t<T> op2(bs_t<T>& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BS, typename T>
|
||||||
|
struct atomic_xor<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||||
|
{
|
||||||
|
using under = typename bs_base<T>::under;
|
||||||
|
|
||||||
|
static inline bs_t<T> op1(bs_t<T>& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline bs_t<T> op2(bs_t<T>& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<bs_t<T>>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct atomic_add<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||||
|
{
|
||||||
|
using under = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
static inline T op1(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline T op2(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct atomic_sub<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||||
|
{
|
||||||
|
using under = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
static inline T op1(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline T op2(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct atomic_and<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||||
|
{
|
||||||
|
using under = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
static inline T op1(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline T op2(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct atomic_xor<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||||
|
{
|
||||||
|
using under = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
static inline T op1(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline T op2(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BS, typename T>
|
||||||
|
struct atomic_test_and_set<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||||
|
{
|
||||||
|
using under = typename bs_base<T>::under;
|
||||||
|
|
||||||
|
static inline bool _op(bs_t<T>& left, T value)
|
||||||
|
{
|
||||||
|
return atomic_storage<under>::bts(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto atomic_op = &_op;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BS, typename T>
|
||||||
|
struct atomic_test_and_reset<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||||
|
{
|
||||||
|
using under = typename bs_base<T>::under;
|
||||||
|
|
||||||
|
static inline bool _op(bs_t<T>& left, T value)
|
||||||
|
{
|
||||||
|
return atomic_storage<under>::btr(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto atomic_op = &_op;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BS, typename T>
|
||||||
|
struct atomic_test_and_complement<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||||
|
{
|
||||||
|
using under = typename bs_base<T>::under;
|
||||||
|
|
||||||
|
static inline bool _op(bs_t<T>& left, T value)
|
||||||
|
{
|
||||||
|
return atomic_storage<under>::btc(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto atomic_op = &_op;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Binary '|' operator: bitwise OR
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
constexpr T operator |(T lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<T>(std::underlying_type_t<T>(lhs) | std::underlying_type_t<T>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary '&' operator: bitwise AND
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
constexpr T operator &(T lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<T>(std::underlying_type_t<T>(lhs) & std::underlying_type_t<T>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary '^' operator: bitwise XOR
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
constexpr T operator ^(T lhs, T rhs)
|
||||||
|
{
|
||||||
|
return static_cast<T>(std::underlying_type_t<T>(lhs) ^ std::underlying_type_t<T>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unary '~' operator: bitwise NEG
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
constexpr T operator ~(T value)
|
||||||
|
{
|
||||||
|
return static_cast<T>(~std::underlying_type_t<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitwise OR assignment
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
inline T& operator |=(T& lhs, T rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<std::underlying_type_t<T>&>(lhs) |= std::underlying_type_t<T>(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitwise AND assignment
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
inline T& operator &=(T& lhs, T rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<std::underlying_type_t<T>&>(lhs) &= std::underlying_type_t<T>(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitwise XOR assignment
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
inline T& operator ^=(T& lhs, T rhs)
|
||||||
|
{
|
||||||
|
reinterpret_cast<std::underlying_type_t<T>&>(lhs) ^= std::underlying_type_t<T>(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
constexpr bool test(T value)
|
||||||
|
{
|
||||||
|
return std::underlying_type_t<T>(value) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
constexpr bool test(T lhs, T rhs)
|
||||||
|
{
|
||||||
|
return (std::underlying_type_t<T>(lhs) & std::underlying_type_t<T>(rhs)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
inline bool test_and_set(T& lhs, T rhs)
|
||||||
|
{
|
||||||
|
return test_and_set(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
inline bool test_and_reset(T& lhs, T rhs)
|
||||||
|
{
|
||||||
|
return test_and_reset(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||||
|
inline bool test_and_complement(T& lhs, T rhs)
|
||||||
|
{
|
||||||
|
return test_and_complement(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct atomic_or<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||||
|
{
|
||||||
|
using under = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
static inline T op1(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline T op2(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct atomic_and<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||||
|
{
|
||||||
|
using under = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
static inline T op1(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline T op2(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct atomic_xor<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||||
|
{
|
||||||
|
using under = std::underlying_type_t<T>;
|
||||||
|
|
||||||
|
static inline T op1(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto fetch_op = &op1;
|
||||||
|
|
||||||
|
static inline T op2(T& left, T right)
|
||||||
|
{
|
||||||
|
return static_cast<T>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto op_fetch = &op2;
|
||||||
|
static constexpr auto atomic_op = &op2;
|
||||||
|
};
|
|
@ -21,9 +21,32 @@ void fmt_class_string<cpu_type>::format(std::string& out, u64 arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void fmt_class_string<bitset_t<cpu_state>::raw_type>::format(std::string& out, u64 arg)
|
void fmt_class_string<cpu_state>::format(std::string& out, u64 arg)
|
||||||
{
|
{
|
||||||
out += "[UNIMPLEMENTED]";
|
format_enum(out, arg, [](cpu_state f)
|
||||||
|
{
|
||||||
|
switch (f)
|
||||||
|
{
|
||||||
|
STR_CASE(cpu_state::stop);
|
||||||
|
STR_CASE(cpu_state::exit);
|
||||||
|
STR_CASE(cpu_state::suspend);
|
||||||
|
STR_CASE(cpu_state::ret);
|
||||||
|
STR_CASE(cpu_state::signal);
|
||||||
|
STR_CASE(cpu_state::dbg_global_pause);
|
||||||
|
STR_CASE(cpu_state::dbg_global_stop);
|
||||||
|
STR_CASE(cpu_state::dbg_pause);
|
||||||
|
STR_CASE(cpu_state::dbg_step);
|
||||||
|
case cpu_state::__bitset_enum_max: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return unknown;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void fmt_class_string<bs_t<cpu_state>>::format(std::string& out, u64 arg)
|
||||||
|
{
|
||||||
|
format_bitset(out, arg, "[", "|", "]", &fmt_class_string<cpu_state>::format);
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local cpu_thread* g_tls_current_cpu_thread = nullptr;
|
thread_local cpu_thread* g_tls_current_cpu_thread = nullptr;
|
||||||
|
@ -39,12 +62,12 @@ void cpu_thread::on_task()
|
||||||
std::unique_lock<named_thread> lock(*this);
|
std::unique_lock<named_thread> lock(*this);
|
||||||
|
|
||||||
// Check thread status
|
// Check thread status
|
||||||
while (!(state & cpu_state::exit))
|
while (!test(state & cpu_state::exit))
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
// check stop status
|
// check stop status
|
||||||
if (!(state & cpu_state::stop))
|
if (!test(state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
if (lock) lock.unlock();
|
if (lock) lock.unlock();
|
||||||
|
|
||||||
|
@ -99,12 +122,12 @@ bool cpu_thread::check_state()
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS; // check at least once
|
CHECK_EMU_STATUS; // check at least once
|
||||||
|
|
||||||
if (state & cpu_state::exit)
|
if (test(state & cpu_state::exit))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.test(cpu_state_pause))
|
if (!test(state & cpu_state_pause))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -120,12 +143,12 @@ bool cpu_thread::check_state()
|
||||||
|
|
||||||
const auto state_ = state.load();
|
const auto state_ = state.load();
|
||||||
|
|
||||||
if (state_ & make_bitset(cpu_state::ret, cpu_state::stop))
|
if (test(state_, cpu_state::ret + cpu_state::stop))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state_ & cpu_state::dbg_step)
|
if (test(state_, cpu_state::dbg_step))
|
||||||
{
|
{
|
||||||
state += cpu_state::dbg_pause;
|
state += cpu_state::dbg_pause;
|
||||||
state -= cpu_state::dbg_step;
|
state -= cpu_state::dbg_step;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../Utilities/Thread.h"
|
#include "../Utilities/Thread.h"
|
||||||
#include "../Utilities/BitSet.h"
|
#include "../Utilities/bit_set.h"
|
||||||
|
|
||||||
// CPU Thread Type (TODO: probably remove, use id and idm to classify threads)
|
// CPU Thread Type (TODO: probably remove, use id and idm to classify threads)
|
||||||
enum class cpu_type : u8
|
enum class cpu_type : u8
|
||||||
|
@ -12,7 +12,7 @@ enum class cpu_type : u8
|
||||||
};
|
};
|
||||||
|
|
||||||
// CPU Thread State flags (TODO: use u32 once cpu_type is removed)
|
// CPU Thread State flags (TODO: use u32 once cpu_type is removed)
|
||||||
enum struct cpu_state : u16
|
enum class cpu_state : u16
|
||||||
{
|
{
|
||||||
stop, // Thread not running (HLE, initial state)
|
stop, // Thread not running (HLE, initial state)
|
||||||
exit, // Irreversible exit
|
exit, // Irreversible exit
|
||||||
|
@ -24,10 +24,12 @@ enum struct cpu_state : u16
|
||||||
dbg_global_stop, // Emulation stopped
|
dbg_global_stop, // Emulation stopped
|
||||||
dbg_pause, // Thread paused
|
dbg_pause, // Thread paused
|
||||||
dbg_step, // Thread forced to pause after one step (one instruction, etc)
|
dbg_step, // Thread forced to pause after one step (one instruction, etc)
|
||||||
|
|
||||||
|
__bitset_enum_max
|
||||||
};
|
};
|
||||||
|
|
||||||
// CPU Thread State flags: pause state union
|
// CPU Thread State flags: pause state union
|
||||||
constexpr bitset_t<cpu_state> cpu_state_pause = make_bitset(cpu_state::suspend, cpu_state::dbg_global_pause, cpu_state::dbg_pause);
|
constexpr bs_t<cpu_state> cpu_state_pause = cpu_state::suspend + cpu_state::dbg_global_pause + cpu_state::dbg_pause;
|
||||||
|
|
||||||
class cpu_thread : public named_thread
|
class cpu_thread : public named_thread
|
||||||
{
|
{
|
||||||
|
@ -43,7 +45,7 @@ public:
|
||||||
cpu_thread(cpu_type type);
|
cpu_thread(cpu_type type);
|
||||||
|
|
||||||
// Public thread state
|
// Public thread state
|
||||||
atomic_t<bitset_t<cpu_state>> state{ cpu_state::stop };
|
atomic_t<bs_t<cpu_state>> state{+cpu_state::stop};
|
||||||
|
|
||||||
// Object associated with sleep state, possibly synchronization primitive (mutex, semaphore, etc.)
|
// Object associated with sleep state, possibly synchronization primitive (mutex, semaphore, etc.)
|
||||||
atomic_t<void*> owner{};
|
atomic_t<void*> owner{};
|
||||||
|
|
|
@ -11,9 +11,30 @@ const ppu_decoder<ppu_itype> s_ppu_itype;
|
||||||
const ppu_decoder<ppu_iname> s_ppu_iname;
|
const ppu_decoder<ppu_iname> s_ppu_iname;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void fmt_class_string<bitset_t<ppu_attr>::raw_type>::format(std::string& out, u64 arg)
|
void fmt_class_string<ppu_attr>::format(std::string& out, u64 arg)
|
||||||
{
|
{
|
||||||
out += "[UNIMPLEMENTED]";
|
format_enum(out, arg, [](ppu_attr value)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case ppu_attr::known_addr: return "known_addr";
|
||||||
|
case ppu_attr::known_size: return "known_size";
|
||||||
|
case ppu_attr::no_return: return "no_return";
|
||||||
|
case ppu_attr::no_size: return "no_size";
|
||||||
|
case ppu_attr::uses_r0: return "uses_r0";
|
||||||
|
case ppu_attr::entry_point: return "entry_point";
|
||||||
|
case ppu_attr::complex_stack: return "complex_stack";
|
||||||
|
case ppu_attr::__bitset_enum_max: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return unknown;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void fmt_class_string<bs_t<ppu_attr>>::format(std::string& out, u64 arg)
|
||||||
|
{
|
||||||
|
format_bitset(out, arg, "[", ",", "]", &fmt_class_string<ppu_attr>::format);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppu_validate(const std::string& fname, const std::vector<ppu_function>& funcs, u32 reloc)
|
void ppu_validate(const std::string& fname, const std::vector<ppu_function>& funcs, u32 reloc)
|
||||||
|
@ -377,7 +398,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
||||||
{
|
{
|
||||||
for (auto it = funcs.lower_bound(addr), end = funcs.end(); it != end; it++)
|
for (auto it = funcs.lower_bound(addr), end = funcs.end(); it != end; it++)
|
||||||
{
|
{
|
||||||
if (it->second.attr & ppu_attr::known_addr)
|
if (test(it->second.attr, ppu_attr::known_addr))
|
||||||
{
|
{
|
||||||
return it->first;
|
return it->first;
|
||||||
}
|
}
|
||||||
|
@ -682,7 +703,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get function limit
|
// Get function limit
|
||||||
const u32 func_end = std::min<u32>(get_limit(func.addr + 1), func.attr & ppu_attr::known_size ? func.addr + func.size : end);
|
const u32 func_end = std::min<u32>(get_limit(func.addr + 1), test(func.attr, ppu_attr::known_size) ? func.addr + func.size : end);
|
||||||
|
|
||||||
// Block analysis workload
|
// Block analysis workload
|
||||||
std::vector<std::reference_wrapper<std::pair<const u32, u32>>> block_queue;
|
std::vector<std::reference_wrapper<std::pair<const u32, u32>>> block_queue;
|
||||||
|
@ -715,7 +736,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: lower priority?
|
// TODO: lower priority?
|
||||||
if (func.attr & ppu_attr::no_size)
|
if (test(func.attr, ppu_attr::no_size))
|
||||||
{
|
{
|
||||||
// Get next function
|
// Get next function
|
||||||
const auto _next = funcs.lower_bound(func.blocks.crbegin()->first + 1);
|
const auto _next = funcs.lower_bound(func.blocks.crbegin()->first + 1);
|
||||||
|
@ -777,12 +798,12 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add next block if necessary
|
// Add next block if necessary
|
||||||
if ((is_call && !pfunc->attr.test(ppu_attr::no_return)) || (type == ppu_itype::BC && (op.bo & 0x14) != 0x14))
|
if ((is_call && !test(pfunc->attr, ppu_attr::no_return)) || (type == ppu_itype::BC && (op.bo & 0x14) != 0x14))
|
||||||
{
|
{
|
||||||
add_block(_ptr.addr());
|
add_block(_ptr.addr());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.lk && (target == iaddr || pfunc->attr.test(ppu_attr::no_return)))
|
if (op.lk && (target == iaddr || test(pfunc->attr, ppu_attr::no_return)))
|
||||||
{
|
{
|
||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
|
@ -843,11 +864,12 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
||||||
if (jt_addr != jt_end && _ptr.addr() == jt_addr)
|
if (jt_addr != jt_end && _ptr.addr() == jt_addr)
|
||||||
{
|
{
|
||||||
// Acknowledge jumptable detection failure
|
// Acknowledge jumptable detection failure
|
||||||
if (!func.attr.test_and_set(ppu_attr::no_size))
|
if (!test(func.attr, ppu_attr::no_size))
|
||||||
{
|
{
|
||||||
LOG_WARNING(PPU, "[0x%x] Jump table not found! 0x%x-0x%x", func.addr, jt_addr, jt_end);
|
LOG_WARNING(PPU, "[0x%x] Jump table not found! 0x%x-0x%x", func.addr, jt_addr, jt_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func.attr += ppu_attr::no_size;
|
||||||
add_block(iaddr);
|
add_block(iaddr);
|
||||||
block_queue.clear();
|
block_queue.clear();
|
||||||
}
|
}
|
||||||
|
@ -871,7 +893,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalization: determine function size
|
// Finalization: determine function size
|
||||||
if (!func.attr.test(ppu_attr::known_size))
|
if (!test(func.attr, ppu_attr::known_size))
|
||||||
{
|
{
|
||||||
const auto last = func.blocks.crbegin();
|
const auto last = func.blocks.crbegin();
|
||||||
|
|
||||||
|
@ -937,7 +959,7 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalization: decrease known function size (TODO)
|
// Finalization: decrease known function size (TODO)
|
||||||
if (func.attr & ppu_attr::known_size)
|
if (test(func.attr, ppu_attr::known_size))
|
||||||
{
|
{
|
||||||
const auto last = func.blocks.crbegin();
|
const auto last = func.blocks.crbegin();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "Utilities/BitSet.h"
|
#include "Utilities/bit_set.h"
|
||||||
#include "Utilities/BEType.h"
|
#include "Utilities/BEType.h"
|
||||||
|
|
||||||
// PPU Function Attributes
|
// PPU Function Attributes
|
||||||
|
@ -16,6 +16,8 @@ enum class ppu_attr : u32
|
||||||
uses_r0,
|
uses_r0,
|
||||||
entry_point,
|
entry_point,
|
||||||
complex_stack,
|
complex_stack,
|
||||||
|
|
||||||
|
__bitset_enum_max
|
||||||
};
|
};
|
||||||
|
|
||||||
// PPU Function Information
|
// PPU Function Information
|
||||||
|
@ -24,7 +26,7 @@ struct ppu_function
|
||||||
u32 addr = 0;
|
u32 addr = 0;
|
||||||
u32 toc = 0;
|
u32 toc = 0;
|
||||||
u32 size = 0;
|
u32 size = 0;
|
||||||
bitset_t<ppu_attr> attr{};
|
bs_t<ppu_attr> attr{};
|
||||||
|
|
||||||
u32 stack_frame = 0;
|
u32 stack_frame = 0;
|
||||||
u32 gate_target = 0;
|
u32 gate_target = 0;
|
||||||
|
|
|
@ -79,9 +79,8 @@ std::string ppu_thread::get_name() const
|
||||||
std::string ppu_thread::dump() const
|
std::string ppu_thread::dump() const
|
||||||
{
|
{
|
||||||
std::string ret;
|
std::string ret;
|
||||||
|
|
||||||
ret += fmt::format("Type: %s\n", typeid(*this).name());
|
ret += fmt::format("Type: %s\n", typeid(*this).name());
|
||||||
ret += fmt::format("State: 0x%08x\n", state.load());
|
ret += fmt::format("State: %s\n", state.load());
|
||||||
ret += fmt::format("Priority: %d\n", prio);
|
ret += fmt::format("Priority: %d\n", prio);
|
||||||
|
|
||||||
ret += "\nRegisters:\n=========\n";
|
ret += "\nRegisters:\n=========\n";
|
||||||
|
@ -196,7 +195,7 @@ void ppu_thread::exec_task()
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (UNLIKELY(state.load()))
|
if (UNLIKELY(test(state)))
|
||||||
{
|
{
|
||||||
if (check_state()) return;
|
if (check_state()) return;
|
||||||
}
|
}
|
||||||
|
@ -228,7 +227,7 @@ void ppu_thread::exec_task()
|
||||||
func2 = table[_i._u32[2]];
|
func2 = table[_i._u32[2]];
|
||||||
func3 = table[_i._u32[3]];
|
func3 = table[_i._u32[3]];
|
||||||
|
|
||||||
if (UNLIKELY(state.load()))
|
if (UNLIKELY(test(state)))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -241,8 +240,6 @@ void ppu_thread::exec_task()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto stop_state = make_bitset(cpu_state::stop, cpu_state::exit, cpu_state::suspend);
|
|
||||||
|
|
||||||
ppu_thread::~ppu_thread()
|
ppu_thread::~ppu_thread()
|
||||||
{
|
{
|
||||||
if (stack_addr)
|
if (stack_addr)
|
||||||
|
@ -311,7 +308,7 @@ ppu_cmd ppu_thread::cmd_wait()
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (UNLIKELY(state.load()))
|
if (UNLIKELY(test(state)))
|
||||||
{
|
{
|
||||||
if (lock) lock.unlock();
|
if (lock) lock.unlock();
|
||||||
|
|
||||||
|
@ -368,7 +365,7 @@ void ppu_thread::fast_call(u32 addr, u32 rtoc)
|
||||||
{
|
{
|
||||||
exec_task();
|
exec_task();
|
||||||
|
|
||||||
if (gpr[1] != old_stack && !state.test(cpu_state::ret) && !state.test(cpu_state::exit)) // gpr[1] shouldn't change
|
if (gpr[1] != old_stack && !test(state, cpu_state::ret + cpu_state::exit)) // gpr[1] shouldn't change
|
||||||
{
|
{
|
||||||
throw fmt::exception("Stack inconsistency (addr=0x%x, rtoc=0x%x, SP=0x%llx, old=0x%llx)", addr, rtoc, gpr[1], old_stack);
|
throw fmt::exception("Stack inconsistency (addr=0x%x, rtoc=0x%x, SP=0x%llx, old=0x%llx)", addr, rtoc, gpr[1], old_stack);
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ Function* PPUTranslator::TranslateToIR(const ppu_function& info, be_t<u32>* bin,
|
||||||
m_base_loaded = m_ir->CreateLoad(m_base);
|
m_base_loaded = m_ir->CreateLoad(m_base);
|
||||||
|
|
||||||
// Non-volatile registers with special meaning (TODO)
|
// Non-volatile registers with special meaning (TODO)
|
||||||
if (info.attr & ppu_attr::uses_r0) m_g_gpr[0] = m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1 + 0, ".r0g");
|
if (test(info.attr, ppu_attr::uses_r0)) m_g_gpr[0] = m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1 + 0, ".r0g");
|
||||||
m_g_gpr[1] = m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1 + 1, ".spg");
|
m_g_gpr[1] = m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1 + 1, ".spg");
|
||||||
m_g_gpr[2] = m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1 + 2, ".rtoc");
|
m_g_gpr[2] = m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1 + 2, ".rtoc");
|
||||||
m_g_gpr[13] = m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1 + 13, ".tls");
|
m_g_gpr[13] = m_ir->CreateConstGEP2_32(nullptr, m_thread, 0, 1 + 13, ".tls");
|
||||||
|
|
|
@ -269,7 +269,7 @@ void spu_recompiler::InterpreterCall(spu_opcode_t op)
|
||||||
|
|
||||||
const u32 old_pc = _spu->pc;
|
const u32 old_pc = _spu->pc;
|
||||||
|
|
||||||
if (_spu->state.load() && _spu->check_state())
|
if (test(_spu->state) && _spu->check_state())
|
||||||
{
|
{
|
||||||
return 0x2000000 | _spu->pc;
|
return 0x2000000 | _spu->pc;
|
||||||
}
|
}
|
||||||
|
@ -340,12 +340,12 @@ void spu_recompiler::FunctionCall()
|
||||||
LOG_ERROR(SPU, "Branch-to-self");
|
LOG_ERROR(SPU, "Branch-to-self");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!_spu->state.load() || !_spu->check_state())
|
while (!test(_spu->state) || !_spu->check_state())
|
||||||
{
|
{
|
||||||
// Proceed recursively
|
// Proceed recursively
|
||||||
spu_recompiler_base::enter(*_spu);
|
spu_recompiler_base::enter(*_spu);
|
||||||
|
|
||||||
if (_spu->state & cpu_state::ret)
|
if (test(_spu->state & cpu_state::ret))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2186,7 +2186,7 @@ void spu_recompiler::BR(spu_opcode_t op)
|
||||||
c->mov(*addr, target | 0x2000000);
|
c->mov(*addr, target | 0x2000000);
|
||||||
//c->cmp(asmjit::host::dword_ptr(*ls, m_pos), 0x32); // compare instruction opcode with BR-to-self
|
//c->cmp(asmjit::host::dword_ptr(*ls, m_pos), 0x32); // compare instruction opcode with BR-to-self
|
||||||
//c->je(labels[target / 4]);
|
//c->je(labels[target / 4]);
|
||||||
c->lock().or_(SPU_OFF_16(state), make_bitset(cpu_state::stop, cpu_state::ret)._value());
|
c->lock().or_(SPU_OFF_16(state), static_cast<u16>(cpu_state::stop + cpu_state::ret));
|
||||||
c->jmp(*end);
|
c->jmp(*end);
|
||||||
c->unuse(*addr);
|
c->unuse(*addr);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -213,7 +213,7 @@ void SPUThread::cpu_task()
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!state.load())
|
if (!test(state))
|
||||||
{
|
{
|
||||||
// Read opcode
|
// Read opcode
|
||||||
const u32 op = base[pc / 4];
|
const u32 op = base[pc / 4];
|
||||||
|
@ -599,9 +599,9 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
|
||||||
{
|
{
|
||||||
if (!channel.try_pop(out))
|
if (!channel.try_pop(out))
|
||||||
{
|
{
|
||||||
thread_lock{*this}, thread_ctrl::wait(WRAP_EXPR(state & cpu_state::stop || channel.try_pop(out)));
|
thread_lock{*this}, thread_ctrl::wait(WRAP_EXPR(test(state & cpu_state::stop) || channel.try_pop(out)));
|
||||||
|
|
||||||
return !state.test(cpu_state::stop);
|
return !test(state & cpu_state::stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -630,7 +630,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
|
||||||
|
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
if (state & cpu_state::stop)
|
if (test(state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -702,14 +702,14 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
|
||||||
if (ch_event_mask & SPU_EVENT_LR)
|
if (ch_event_mask & SPU_EVENT_LR)
|
||||||
{
|
{
|
||||||
// register waiter if polling reservation status is required
|
// register waiter if polling reservation status is required
|
||||||
vm::wait_op(last_raddr, 128, WRAP_EXPR(get_events(true) || state & cpu_state::stop));
|
vm::wait_op(last_raddr, 128, WRAP_EXPR(get_events(true) || test(state & cpu_state::stop)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lock.lock();
|
lock.lock();
|
||||||
|
|
||||||
// simple waiting loop otherwise
|
// simple waiting loop otherwise
|
||||||
while (!get_events(true) && !(state & cpu_state::stop))
|
while (!get_events(true) && !test(state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
|
@ -719,7 +719,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
|
||||||
|
|
||||||
ch_event_stat &= ~SPU_EVENT_WAITING;
|
ch_event_stat &= ~SPU_EVENT_WAITING;
|
||||||
|
|
||||||
if (state & cpu_state::stop)
|
if (test(state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -759,7 +759,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
if (state & cpu_state::stop)
|
if (test(state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -966,7 +966,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
if (state & cpu_state::stop)
|
if (test(state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1231,7 +1231,7 @@ bool SPUThread::stop_and_signal(u32 code)
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
if (state & cpu_state::stop)
|
if (test(state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1272,7 +1272,7 @@ bool SPUThread::stop_and_signal(u32 code)
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
if (state & cpu_state::stop)
|
if (test(state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ ppu_error_code sys_fs_open(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32
|
||||||
return CELL_EISDIR;
|
return CELL_EISDIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
bitset_t<fs::open_mode> open_mode{};
|
bs_t<fs::open_mode> open_mode{};
|
||||||
|
|
||||||
switch (flags & CELL_FS_O_ACCMODE)
|
switch (flags & CELL_FS_O_ACCMODE)
|
||||||
{
|
{
|
||||||
|
@ -125,7 +125,7 @@ ppu_error_code sys_fs_open(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32
|
||||||
open_mode = {}; // error
|
open_mode = {}; // error
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!open_mode)
|
if (!test(open_mode))
|
||||||
{
|
{
|
||||||
throw EXCEPTION("Invalid or unimplemented flags (%#o): '%s'", flags, path.get_ptr());
|
throw EXCEPTION("Invalid or unimplemented flags (%#o): '%s'", flags, path.get_ptr());
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ ppu_error_code sys_fs_open(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32
|
||||||
{
|
{
|
||||||
sys_fs.error("sys_fs_open('%s'): failed to open file (flags=%#o, mode=%#o)", path.get_ptr(), flags, mode);
|
sys_fs.error("sys_fs_open('%s'): failed to open file (flags=%#o, mode=%#o)", path.get_ptr(), flags, mode);
|
||||||
|
|
||||||
if (open_mode & fs::excl)
|
if (test(open_mode & fs::excl))
|
||||||
{
|
{
|
||||||
return CELL_EEXIST; // approximation
|
return CELL_EEXIST; // approximation
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ void lv2_int_serv_t::join(ppu_thread& ppu, lv2_lock_t lv2_lock)
|
||||||
thread->lock_notify();
|
thread->lock_notify();
|
||||||
|
|
||||||
// Join thread (TODO)
|
// Join thread (TODO)
|
||||||
while (!(thread->state & cpu_state::exit))
|
while (!test(thread->state & cpu_state::exit))
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread
|
||||||
}
|
}
|
||||||
|
|
||||||
// If interrupt thread is running, it's already established on another interrupt tag
|
// If interrupt thread is running, it's already established on another interrupt tag
|
||||||
if (!(it->state & cpu_state::stop))
|
if (!test(it->state & cpu_state::stop))
|
||||||
{
|
{
|
||||||
return CELL_EAGAIN;
|
return CELL_EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ s32 sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr<u64> vptr)
|
||||||
thread->is_joining = true;
|
thread->is_joining = true;
|
||||||
|
|
||||||
// join thread
|
// join thread
|
||||||
while (!(thread->state & cpu_state::exit))
|
while (!test(thread->state & cpu_state::exit))
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ void ARMv7Thread::cpu_task_main()
|
||||||
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->PC);
|
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->PC);
|
||||||
};
|
};
|
||||||
|
|
||||||
while (!state.load() || !check_state())
|
while (!test(state) || !check_state())
|
||||||
{
|
{
|
||||||
if (ISET == Thumb)
|
if (ISET == Thumb)
|
||||||
{
|
{
|
||||||
|
@ -142,7 +142,7 @@ void ARMv7Thread::fast_call(u32 addr)
|
||||||
{
|
{
|
||||||
cpu_task_main();
|
cpu_task_main();
|
||||||
|
|
||||||
if (SP != old_SP && !state.test(cpu_state::ret) && !state.test(cpu_state::exit)) // SP shouldn't change
|
if (SP != old_SP && !test(state, cpu_state::ret + cpu_state::exit)) // SP shouldn't change
|
||||||
{
|
{
|
||||||
throw fmt::exception("Stack inconsistency (addr=0x%x, SP=0x%x, old=0x%x)", addr, SP, old_SP);
|
throw fmt::exception("Stack inconsistency (addr=0x%x, SP=0x%x, old=0x%x)", addr, SP, old_SP);
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,7 @@ void InterpreterDisAsmFrame::ShowAddr(u32 addr)
|
||||||
|
|
||||||
wxColour colour;
|
wxColour colour;
|
||||||
|
|
||||||
if (cpu->state.test(cpu_state_pause) && m_pc == GetPc())
|
if (test(cpu->state & cpu_state_pause) && m_pc == GetPc())
|
||||||
{
|
{
|
||||||
colour = wxColour("Green");
|
colour = wxColour("Green");
|
||||||
}
|
}
|
||||||
|
@ -436,7 +436,7 @@ void InterpreterDisAsmFrame::Show_PC(wxCommandEvent& WXUNUSED(event))
|
||||||
|
|
||||||
void InterpreterDisAsmFrame::DoRun(wxCommandEvent& WXUNUSED(event))
|
void InterpreterDisAsmFrame::DoRun(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
if (cpu && cpu->state.test(cpu_state_pause))
|
if (cpu && test(cpu->state & cpu_state_pause))
|
||||||
{
|
{
|
||||||
cpu->state -= cpu_state::dbg_pause;
|
cpu->state -= cpu_state::dbg_pause;
|
||||||
(*cpu)->lock_notify();
|
(*cpu)->lock_notify();
|
||||||
|
@ -455,11 +455,11 @@ void InterpreterDisAsmFrame::DoStep(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
if (cpu)
|
if (cpu)
|
||||||
{
|
{
|
||||||
if (cpu->state.atomic_op([](bitset_t<cpu_state>& state) -> bool
|
if (test(cpu_state::dbg_pause, cpu->state.fetch_op([](bs_t<cpu_state>& state)
|
||||||
{
|
{
|
||||||
state += cpu_state::dbg_step;
|
state += cpu_state::dbg_step;
|
||||||
return state.test_and_reset(cpu_state::dbg_pause);
|
state -= cpu_state::dbg_pause;
|
||||||
}))
|
})))
|
||||||
{
|
{
|
||||||
(*cpu)->lock_notify();
|
(*cpu)->lock_notify();
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,7 +386,7 @@
|
||||||
<ClInclude Include="..\Utilities\AutoPause.h" />
|
<ClInclude Include="..\Utilities\AutoPause.h" />
|
||||||
<ClInclude Include="..\Utilities\BEType.h" />
|
<ClInclude Include="..\Utilities\BEType.h" />
|
||||||
<ClInclude Include="..\Utilities\BitField.h" />
|
<ClInclude Include="..\Utilities\BitField.h" />
|
||||||
<ClInclude Include="..\Utilities\BitSet.h" />
|
<ClInclude Include="..\Utilities\bit_set.h" />
|
||||||
<ClInclude Include="..\Utilities\cfmt.h" />
|
<ClInclude Include="..\Utilities\cfmt.h" />
|
||||||
<ClInclude Include="..\Utilities\dynamic_library.h" />
|
<ClInclude Include="..\Utilities\dynamic_library.h" />
|
||||||
<ClInclude Include="..\Utilities\event.h" />
|
<ClInclude Include="..\Utilities\event.h" />
|
||||||
|
|
|
@ -1453,9 +1453,6 @@
|
||||||
<ClInclude Include="..\Utilities\geometry.h">
|
<ClInclude Include="..\Utilities\geometry.h">
|
||||||
<Filter>Utilities</Filter>
|
<Filter>Utilities</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\Utilities\BitSet.h">
|
|
||||||
<Filter>Utilities</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Emu\PSP2\ARMv7Callback.h">
|
<ClInclude Include="Emu\PSP2\ARMv7Callback.h">
|
||||||
<Filter>Emu\PSP2</Filter>
|
<Filter>Emu\PSP2</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -1708,5 +1705,8 @@
|
||||||
<ClInclude Include="..\Utilities\cfmt.h">
|
<ClInclude Include="..\Utilities\cfmt.h">
|
||||||
<Filter>Utilities</Filter>
|
<Filter>Utilities</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\Utilities\bit_set.h">
|
||||||
|
<Filter>Utilities</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Loading…
Add table
Add a link
Reference in a new issue