chore: remove vector bsearch code, seperate OptionalRef
to new `lucore` project.
This commit is contained in:
parent
43d0ac9630
commit
a8fb72804c
|
@ -3,6 +3,8 @@ project(lcpu-native
|
|||
DESCRIPTION "Superproject for LCPU GMOD Native Module"
|
||||
)
|
||||
|
||||
add_subdirectory(lucore)
|
||||
|
||||
# RISC-V emulation library
|
||||
add_subdirectory(riscv)
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
project(lucore
|
||||
DESCRIPTION "lucore - shared core stuff for the lcpu native projects"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
add_library(lucore INTERFACE)
|
||||
|
||||
target_compile_features(lucore INTERFACE cxx_std_20)
|
||||
target_include_directories(lucore INTERFACE ${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
add_library(lucore::lucore ALIAS lucore)
|
|
@ -0,0 +1,16 @@
|
|||
//! Lucore Assert Wrappers
|
||||
|
||||
// this just plumbs everything to libc by default
|
||||
// your project can choose to configure its own assertion handler by
|
||||
// defining LUCORE_ASSERT()
|
||||
// TODO: LUCORE_VERIFY/plumbing this into our own assertion code?
|
||||
#ifndef LUCORE_ASSERT
|
||||
#include <cassert>
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define LUCORE_ASSERT(...) assert(__VA_ARGS__)
|
||||
#else
|
||||
#define LUCORE_ASSERT(...)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,70 @@
|
|||
//! OptionalRef - std::optional<T&> for C++20
|
||||
|
||||
#include <lucore/Assert.hpp>
|
||||
|
||||
namespace lucore::detail {
|
||||
|
||||
struct Nullref_t {};
|
||||
|
||||
template <class T>
|
||||
struct OptionalRef; // sfinae on non-reference types
|
||||
|
||||
/// Like std::optional<T>, but optimized specifically for references to objects.
|
||||
///
|
||||
/// Additionally, as a bonus, since the repressentation is so simple, this is
|
||||
/// constexpr-safe by default, so it can be relatively well optimized.
|
||||
///
|
||||
/// # Safety
|
||||
/// Treat this class like [std::reference_wrapper].
|
||||
/// Do *not* give this class invalid references.
|
||||
template <class T>
|
||||
struct OptionalRef<T&> {
|
||||
constexpr OptionalRef() : ptr(nullptr) {
|
||||
}
|
||||
|
||||
// trigger explicit null construction
|
||||
constexpr OptionalRef(Nullref_t) : OptionalRef() {
|
||||
}
|
||||
|
||||
constexpr OptionalRef(T& ref) : ptr(&ref) {
|
||||
}
|
||||
|
||||
// polymorphic downconstruction from another OptionalRef<U>
|
||||
template <class U>
|
||||
constexpr OptionalRef(const OptionalRef<U&>& other) : ptr(&other.ptr) {
|
||||
}
|
||||
|
||||
constexpr T& Value() const {
|
||||
LUCORE_ASSERT(HasValue() && "Attempt to access OptionalRef without stored value!");
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
constexpr bool HasValue() const {
|
||||
return ptr != nullptr;
|
||||
}
|
||||
|
||||
constexpr T& operator*() const {
|
||||
return Value();
|
||||
}
|
||||
|
||||
// unchecked access: DO NOT use this without checking beforehand
|
||||
constexpr T* operator->() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
constexpr operator bool() const {
|
||||
return HasValue();
|
||||
}
|
||||
|
||||
private:
|
||||
T* ptr {};
|
||||
};
|
||||
|
||||
/// Sentinel value to explicitly not populate an OptionalRef.
|
||||
inline static Nullref_t NullRef {};
|
||||
} // namespace lucore::detail
|
||||
|
||||
namespace lucore {
|
||||
using detail::NullRef;
|
||||
using detail::OptionalRef;
|
||||
} // namespace lucore
|
|
@ -12,4 +12,9 @@ add_library(riscv
|
|||
target_compile_features(riscv PUBLIC cxx_std_20)
|
||||
target_include_directories(riscv PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
target_link_libraries(riscv PUBLIC
|
||||
# lucore is a public dependency, since it is (techinically) used in public api surfaces
|
||||
lucore::lucore
|
||||
)
|
||||
|
||||
add_library(riscv::riscv ALIAS riscv)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <riscv/Types.hpp>
|
||||
#include <lucore/OptionalRef.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
@ -26,8 +27,8 @@ namespace riscv {
|
|||
/// Is this device clocked?
|
||||
virtual bool Clocked() const { return false; }
|
||||
|
||||
/// This function is called during clocks to give clocked devices
|
||||
/// the ability to update
|
||||
/// This function is called to give clocked devices
|
||||
/// the ability to... well, clock!
|
||||
virtual void Clock() {}
|
||||
|
||||
// TODO(feat): Need to implement ability to generate interrupts
|
||||
|
@ -47,9 +48,13 @@ namespace riscv {
|
|||
|
||||
};
|
||||
|
||||
~Bus();
|
||||
|
||||
/// Attach a device to the bus.
|
||||
///
|
||||
/// Note that once this function is called (and the device is successfully added),
|
||||
/// the object pointed to by [device] is owned by the Bus object, and should not be deleted.
|
||||
///
|
||||
/// # Returns
|
||||
/// This function returns true if the device was able to be put on the bus.
|
||||
/// This function returns false in the following error cases:
|
||||
|
@ -70,21 +75,12 @@ namespace riscv {
|
|||
void PokeWord(AddressT address, u32 value);
|
||||
private:
|
||||
|
||||
OptionalRef<Device&> FindDeviceForAddress(AddressT address) const;
|
||||
lucore::OptionalRef<Device&> FindDeviceForAddress(AddressT address) const;
|
||||
|
||||
CPU* attachedCpu{};
|
||||
|
||||
// TODO: if this ends up being a hotpath replace with robinhood unordered map
|
||||
std::unordered_map<AddressT, Device*> mapped_devices;
|
||||
|
||||
// this is essentially a flat map
|
||||
// we don't particularly need hashing :p
|
||||
//struct AttachedDevice {
|
||||
// AddressT baseAddress{};
|
||||
// Device* device{};
|
||||
//};
|
||||
|
||||
//std::vector<AttachedDevice> mapped_devices{};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -21,69 +21,6 @@ namespace riscv {
|
|||
/// A type that can repressent address space or address space offsets.
|
||||
using AddressT = u32;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct Nullref_t {};
|
||||
|
||||
template <class T>
|
||||
struct OptionalRef; // sfinae on non-reference types
|
||||
|
||||
/// Like std::optional, but for references to objects.
|
||||
///
|
||||
/// Additionally, as a bonus, since the repressentation is so simple, this is
|
||||
/// constexpr-safe by default, so it can be relatively well optimized.
|
||||
///
|
||||
/// # Safety
|
||||
/// Treat this class like [std::reference_wrapper].
|
||||
/// Do *not* give this class invalid references.
|
||||
template <class T>
|
||||
struct OptionalRef<T&> {
|
||||
constexpr OptionalRef() : ptr(nullptr) {
|
||||
}
|
||||
|
||||
// trigger explicit null construction
|
||||
constexpr OptionalRef(Nullref_t) : OptionalRef() {
|
||||
}
|
||||
|
||||
constexpr OptionalRef(T& ref) : ptr(&ref) {
|
||||
}
|
||||
|
||||
// polymorphic downconstruction from another OptionalRef<U>
|
||||
template <class U>
|
||||
constexpr OptionalRef(const OptionalRef<U&>& other) : ptr(&other.ptr) {
|
||||
}
|
||||
|
||||
constexpr T& Value() const {
|
||||
assert(HasValue() && "Attempt to access OptionalRef without stored value!");
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
constexpr bool HasValue() const {
|
||||
return ptr != nullptr;
|
||||
}
|
||||
|
||||
constexpr T& operator*() const {
|
||||
return Value();
|
||||
}
|
||||
|
||||
// unchecked access: DO NOT use this without checking beforehand
|
||||
constexpr T* operator->() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
constexpr operator bool() const {
|
||||
return HasValue();
|
||||
}
|
||||
|
||||
private:
|
||||
T* ptr {};
|
||||
};
|
||||
|
||||
/// Sentinel value to explicitly not populate an OptionalRef.
|
||||
inline static Nullref_t NullRef {};
|
||||
} // namespace detail
|
||||
|
||||
using detail::NullRef;
|
||||
using detail::OptionalRef;
|
||||
|
||||
|
||||
} // namespace riscv
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
#include <algorithm>
|
||||
#include <riscv/Bus.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace riscv {
|
||||
|
||||
Bus::~Bus() {
|
||||
// Free all devices
|
||||
for(auto& pair : mapped_devices)
|
||||
delete pair.second;
|
||||
}
|
||||
|
||||
bool Bus::AttachDevice(AddressT baseAddress, Device* device) {
|
||||
if(!device)
|
||||
return false;
|
||||
|
@ -15,9 +20,7 @@ namespace riscv {
|
|||
else if(FindDeviceForAddress(baseAddress + device->Size()))
|
||||
return false;
|
||||
|
||||
//mapped_devices.push_back({ .baseAddress = baseAddress, .device = device });
|
||||
mapped_devices[baseAddress] = device;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -61,33 +64,18 @@ namespace riscv {
|
|||
return opt->PokeWord(address - opt->BaseAddress(), value);
|
||||
}
|
||||
|
||||
OptionalRef<Bus::Device&> Bus::FindDeviceForAddress(AddressT address) const {
|
||||
/*
|
||||
for(auto& device : mapped_devices) {
|
||||
// If the requested address directly matches the base address of a device
|
||||
// mapped into memory, then we do not even need to consider checking the layout.
|
||||
if(device.baseAddress == address)
|
||||
return *device.device;
|
||||
|
||||
// Otherwise, we *do* unfortunately have to do so.
|
||||
if(address >= device.baseAddress &&
|
||||
address < device.baseAddress + device.device->Size())
|
||||
return *device.device;
|
||||
}*/
|
||||
|
||||
lucore::OptionalRef<Bus::Device&> Bus::FindDeviceForAddress(AddressT address) const {
|
||||
auto it = std::find_if(mapped_devices.begin(), mapped_devices.end(), [&](const auto& pair) {
|
||||
return
|
||||
// We can shorcut the region checking if the requested addess matches base address.
|
||||
pair.first == address ||
|
||||
// If it doesn't we really can't, though.
|
||||
(address >= pair.first &&
|
||||
address < pair.first + pair.second->Size());
|
||||
return
|
||||
// We can shorcut the region checking if the requested addess matches base address.
|
||||
pair.first == address ||
|
||||
// If it doesn't we really can't, though.
|
||||
(address >= pair.first && address < pair.first + pair.second->Size());
|
||||
});
|
||||
|
||||
|
||||
// No device was found at this address
|
||||
if(it == mapped_devices.end())
|
||||
return NullRef;
|
||||
return lucore::NullRef;
|
||||
else
|
||||
return *it->second;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue