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"
|
DESCRIPTION "Superproject for LCPU GMOD Native Module"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(lucore)
|
||||||
|
|
||||||
# RISC-V emulation library
|
# RISC-V emulation library
|
||||||
add_subdirectory(riscv)
|
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_compile_features(riscv PUBLIC cxx_std_20)
|
||||||
target_include_directories(riscv PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
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)
|
add_library(riscv::riscv ALIAS riscv)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <riscv/Types.hpp>
|
#include <riscv/Types.hpp>
|
||||||
|
#include <lucore/OptionalRef.hpp>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@ -26,8 +27,8 @@ namespace riscv {
|
||||||
/// Is this device clocked?
|
/// Is this device clocked?
|
||||||
virtual bool Clocked() const { return false; }
|
virtual bool Clocked() const { return false; }
|
||||||
|
|
||||||
/// This function is called during clocks to give clocked devices
|
/// This function is called to give clocked devices
|
||||||
/// the ability to update
|
/// the ability to... well, clock!
|
||||||
virtual void Clock() {}
|
virtual void Clock() {}
|
||||||
|
|
||||||
// TODO(feat): Need to implement ability to generate interrupts
|
// TODO(feat): Need to implement ability to generate interrupts
|
||||||
|
@ -47,9 +48,13 @@ namespace riscv {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
~Bus();
|
||||||
|
|
||||||
/// Attach a device to the 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
|
/// # Returns
|
||||||
/// This function returns true if the device was able to be put on the bus.
|
/// This function returns true if the device was able to be put on the bus.
|
||||||
/// This function returns false in the following error cases:
|
/// This function returns false in the following error cases:
|
||||||
|
@ -70,21 +75,12 @@ namespace riscv {
|
||||||
void PokeWord(AddressT address, u32 value);
|
void PokeWord(AddressT address, u32 value);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
OptionalRef<Device&> FindDeviceForAddress(AddressT address) const;
|
lucore::OptionalRef<Device&> FindDeviceForAddress(AddressT address) const;
|
||||||
|
|
||||||
CPU* attachedCpu{};
|
CPU* attachedCpu{};
|
||||||
|
|
||||||
// TODO: if this ends up being a hotpath replace with robinhood unordered map
|
// TODO: if this ends up being a hotpath replace with robinhood unordered map
|
||||||
std::unordered_map<AddressT, Device*> mapped_devices;
|
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.
|
/// A type that can repressent address space or address space offsets.
|
||||||
using AddressT = u32;
|
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
|
} // namespace riscv
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
|
#include <algorithm>
|
||||||
#include <riscv/Bus.hpp>
|
#include <riscv/Bus.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace riscv {
|
namespace riscv {
|
||||||
|
|
||||||
|
Bus::~Bus() {
|
||||||
|
// Free all devices
|
||||||
|
for(auto& pair : mapped_devices)
|
||||||
|
delete pair.second;
|
||||||
|
}
|
||||||
|
|
||||||
bool Bus::AttachDevice(AddressT baseAddress, Device* device) {
|
bool Bus::AttachDevice(AddressT baseAddress, Device* device) {
|
||||||
if(!device)
|
if(!device)
|
||||||
return false;
|
return false;
|
||||||
|
@ -15,9 +20,7 @@ namespace riscv {
|
||||||
else if(FindDeviceForAddress(baseAddress + device->Size()))
|
else if(FindDeviceForAddress(baseAddress + device->Size()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
//mapped_devices.push_back({ .baseAddress = baseAddress, .device = device });
|
|
||||||
mapped_devices[baseAddress] = device;
|
mapped_devices[baseAddress] = device;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,33 +64,18 @@ namespace riscv {
|
||||||
return opt->PokeWord(address - opt->BaseAddress(), value);
|
return opt->PokeWord(address - opt->BaseAddress(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionalRef<Bus::Device&> Bus::FindDeviceForAddress(AddressT address) const {
|
lucore::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;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
auto it = std::find_if(mapped_devices.begin(), mapped_devices.end(), [&](const auto& pair) {
|
auto it = std::find_if(mapped_devices.begin(), mapped_devices.end(), [&](const auto& pair) {
|
||||||
return
|
return
|
||||||
// We can shorcut the region checking if the requested addess matches base address.
|
// We can shorcut the region checking if the requested addess matches base address.
|
||||||
pair.first == address ||
|
pair.first == address ||
|
||||||
// If it doesn't we really can't, though.
|
// If it doesn't we really can't, though.
|
||||||
(address >= pair.first &&
|
(address >= pair.first && address < pair.first + pair.second->Size());
|
||||||
address < pair.first + pair.second->Size());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// No device was found at this address
|
// No device was found at this address
|
||||||
if(it == mapped_devices.end())
|
if(it == mapped_devices.end())
|
||||||
return NullRef;
|
return lucore::NullRef;
|
||||||
else
|
else
|
||||||
return *it->second;
|
return *it->second;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue