mirror of
https://github.com/LNH-team/pico-launcher.git
synced 2026-06-02 09:06:54 +02:00
Initial commit
This commit is contained in:
53
arm9/source/core/BitVector.h
Normal file
53
arm9/source/core/BitVector.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
|
||||
template <u32 Length>
|
||||
class BitVector
|
||||
{
|
||||
public:
|
||||
BitVector()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
u32 Get(u32 idx) const
|
||||
{
|
||||
return (_flags[idx >> 5] >> (idx & 0x1F)) & 1;
|
||||
}
|
||||
|
||||
void Set(u32 idx, u32 value)
|
||||
{
|
||||
u32 f = _flags[idx >> 5];
|
||||
if (value)
|
||||
f |= 1 << (idx & 0x1F);
|
||||
else
|
||||
f &= ~(1 << (idx & 0x1F));
|
||||
_flags[idx >> 5] = f;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
_flags.fill(0);
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
constexpr int FindFirstZero()
|
||||
{
|
||||
u32 globalBitNr = 0;
|
||||
for (const u32 bits : _flags)
|
||||
{
|
||||
if (bits == ~0u)
|
||||
{
|
||||
globalBitNr += 32;
|
||||
continue;
|
||||
}
|
||||
u32 invBits = ~bits;
|
||||
globalBitNr += 31 - __builtin_clz(-invBits & invBits);
|
||||
return globalBitNr >= Length ? -1 : globalBitNr;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<u32, (Length + 31) / 32> _flags;
|
||||
};
|
||||
64
arm9/source/core/Environment.cpp
Normal file
64
arm9/source/core/Environment.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <nds.h>
|
||||
#include "picoAgbAdapter.h"
|
||||
#include "Environment.h"
|
||||
|
||||
u32 Environment::_flags;
|
||||
|
||||
static bool detectIsNitroEmulator()
|
||||
{
|
||||
u32 agbMemoryAddress = *(vu32*)0x027FFF7C;
|
||||
if (agbMemoryAddress < 0x08000000 || agbMemoryAddress >= 0x0A000000)
|
||||
return false;
|
||||
u32 monitorRomLoadAddress = *(vu32*)0x027FFF68;
|
||||
if (monitorRomLoadAddress < 0x02000000 || monitorRomLoadAddress >= 0x02800000)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool detectNocashPrintSuppport()
|
||||
{
|
||||
vu8* identifier = (vu8*)0x04FFFA00;
|
||||
// melon ds only seems to implement this for 8 bit reads
|
||||
u32 nocashIdentifier = identifier[0] | (identifier[1] << 8) | (identifier[2] << 16) | (identifier[3] << 24);
|
||||
return nocashIdentifier == 0x67246F6E // no$g
|
||||
|| nocashIdentifier == 0x6F6C656D; // melo
|
||||
}
|
||||
|
||||
static bool detectPicoAgbAdapter()
|
||||
{
|
||||
REG_EXMEMCNT &= ~0xFF;
|
||||
return PICO_AGB_IDENTIFIER == PICO_AGB_IDENTIFIER_VALUE;
|
||||
}
|
||||
|
||||
void Environment::Initialize()
|
||||
{
|
||||
_flags = ENVIRONMENT_FLAGS_NONE;
|
||||
if (isDSiMode())
|
||||
{
|
||||
_flags |= ENVIRONMENT_FLAGS_DSI_MODE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (detectIsNitroEmulator())
|
||||
{
|
||||
_flags |= ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR;
|
||||
_flags |= ENVIRONMENT_FLAGS_JTAG_SEMIHOSTING;
|
||||
|
||||
REG_EXMEMCNT &= ~0xFF;
|
||||
|
||||
u32 agbMemoryAddress = *(vu32*)0x027FFF7C;
|
||||
if (*(vu32*)(agbMemoryAddress + 0x100) == 0x44495349) //ISID
|
||||
_flags |= ENVIRONMENT_FLAGS_AGB_SEMIHOSTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (detectPicoAgbAdapter())
|
||||
_flags |= ENVIRONMENT_FLAGS_PICO_AGB_ADAPTER;
|
||||
}
|
||||
}
|
||||
if (!(_flags & ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR))
|
||||
{
|
||||
if (detectNocashPrintSuppport())
|
||||
_flags |= ENVIRONMENT_FLAGS_NOCASH_PRINT;
|
||||
}
|
||||
}
|
||||
32
arm9/source/core/Environment.h
Normal file
32
arm9/source/core/Environment.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
class Environment
|
||||
{
|
||||
enum EnvironmentFlags : u32
|
||||
{
|
||||
ENVIRONMENT_FLAGS_NONE = 0,
|
||||
|
||||
ENVIRONMENT_FLAGS_DSI_MODE = (1 << 0),
|
||||
ENVIRONMENT_FLAGS_NOCASH_PRINT = (1 << 1),
|
||||
ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR = (1 << 2),
|
||||
ENVIRONMENT_FLAGS_JTAG_SEMIHOSTING = (1 << 3),
|
||||
ENVIRONMENT_FLAGS_AGB_SEMIHOSTING = (1 << 4),
|
||||
ENVIRONMENT_FLAGS_DLDI = (1 << 5),
|
||||
ENVIRONMENT_FLAGS_ARGV = (1 << 6),
|
||||
ENVIRONMENT_FLAGS_PICO_AGB_ADAPTER = (1 << 7)
|
||||
};
|
||||
|
||||
static u32 _flags;
|
||||
|
||||
public:
|
||||
static void Initialize();
|
||||
|
||||
static inline bool IsDsiMode() { return _flags & ENVIRONMENT_FLAGS_DSI_MODE; }
|
||||
static inline bool SupportsNocashPrint() { return _flags & ENVIRONMENT_FLAGS_NOCASH_PRINT; }
|
||||
static inline bool IsIsNitroEmulator() { return _flags & ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR; }
|
||||
static inline bool SupportsJtagSemihosting() { return _flags & ENVIRONMENT_FLAGS_JTAG_SEMIHOSTING; }
|
||||
static inline bool SupportsAgbSemihosting() { return _flags & ENVIRONMENT_FLAGS_AGB_SEMIHOSTING; }
|
||||
static inline bool SupportsDldi() { return _flags & ENVIRONMENT_FLAGS_DLDI; }
|
||||
static inline bool SupportsArgv() { return _flags & ENVIRONMENT_FLAGS_ARGV; }
|
||||
static inline bool HasPicoAgbAdapter() { return _flags & ENVIRONMENT_FLAGS_PICO_AGB_ADAPTER; }
|
||||
};
|
||||
189
arm9/source/core/LinkedList.h
Normal file
189
arm9/source/core/LinkedList.h
Normal file
@@ -0,0 +1,189 @@
|
||||
#pragma once
|
||||
#include "LinkedListLink.h"
|
||||
|
||||
/// @brief Class implementing a linked list with the link inside the list items.
|
||||
/// @tparam T The type of list items.
|
||||
/// @tparam member The link inside the items type.
|
||||
template <typename T, LinkedListLink T::*member>
|
||||
class LinkedList
|
||||
{
|
||||
public:
|
||||
/// @brief Linked list iterator.
|
||||
class Iterator
|
||||
{
|
||||
LinkedListLink* _itemLink;
|
||||
|
||||
public:
|
||||
/// @brief Creates an iterator pointing to the given item.
|
||||
/// @param itemLink The link of the item this iterator points to.
|
||||
explicit Iterator(LinkedListLink* itemLink)
|
||||
: _itemLink(itemLink) { }
|
||||
|
||||
/// @brief Checks whether this iterator is inequal to rhs.
|
||||
/// @param rhs The iterator to compare to.
|
||||
/// @return True if the iterators are inequal, or false otherwise.
|
||||
constexpr bool operator!=(Iterator rhs) const
|
||||
{
|
||||
return _itemLink != rhs._itemLink;
|
||||
}
|
||||
|
||||
/// @brief Gets the list item this iterator points to.
|
||||
/// @return A reference to the list item.
|
||||
constexpr T& operator*() const
|
||||
{
|
||||
return *LinkToItem(_itemLink);
|
||||
}
|
||||
|
||||
/// @brief Advances this iterator to the next list item.
|
||||
void operator++()
|
||||
{
|
||||
_itemLink = _itemLink->next;
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Inserts the given item at the head of the list.
|
||||
/// @param item The item to insert.
|
||||
void InsertHead(T* item)
|
||||
{
|
||||
LinkedListLink* link = &(item->*member);
|
||||
link->next = _head;
|
||||
link->prev = nullptr;
|
||||
if (_head)
|
||||
{
|
||||
_head->prev = link;
|
||||
}
|
||||
_head = link;
|
||||
if (!_count++)
|
||||
{
|
||||
_tail = link;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Inserts the given item at the tail of the list.
|
||||
/// @param item The item to insert.
|
||||
void InsertTail(T* item)
|
||||
{
|
||||
LinkedListLink* link = &(item->*member);
|
||||
link->next = nullptr;
|
||||
link->prev = _tail;
|
||||
if (_tail)
|
||||
{
|
||||
_tail->next = link;
|
||||
}
|
||||
_tail = link;
|
||||
if (!_count++)
|
||||
{
|
||||
_head = link;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Inserts the given item after the other item.
|
||||
/// @param item The item to insert.
|
||||
/// @param after The item after which will be inserted.
|
||||
void InsertAfter(T* item, T* after)
|
||||
{
|
||||
LinkedListLink* itemLink = &(item->*member);
|
||||
LinkedListLink* afterLink = &(after->*member);
|
||||
itemLink->next = afterLink->next;
|
||||
itemLink->prev = afterLink;
|
||||
if (afterLink->next)
|
||||
{
|
||||
afterLink->next->prev = itemLink;
|
||||
}
|
||||
_count++;
|
||||
}
|
||||
|
||||
/// @brief Inserts the given item before the other item.
|
||||
/// @param item The item to insert.
|
||||
/// @param after The item before which will be inserted.
|
||||
void InsertBefore(T* item, T* before)
|
||||
{
|
||||
LinkedListLink* itemLink = &(item->*member);
|
||||
LinkedListLink* beforeLink = &(before->*member);
|
||||
itemLink->next = beforeLink;
|
||||
itemLink->prev = beforeLink->prev;
|
||||
if (beforeLink->prev)
|
||||
{
|
||||
beforeLink->prev->next = itemLink;
|
||||
}
|
||||
_count++;
|
||||
}
|
||||
|
||||
/// @brief Removes the given item from the list.
|
||||
/// @param item The item to remove.
|
||||
void Remove(T* item)
|
||||
{
|
||||
LinkedListLink* itemLink = &(item->*member);
|
||||
if (!itemLink->prev)
|
||||
{
|
||||
_head = itemLink->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemLink->prev->next = itemLink->next;
|
||||
}
|
||||
|
||||
if (!itemLink->next)
|
||||
{
|
||||
_tail = itemLink->prev;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemLink->next->prev = itemLink->prev;
|
||||
}
|
||||
itemLink->prev = nullptr;
|
||||
itemLink->next = nullptr;
|
||||
_count--;
|
||||
}
|
||||
|
||||
/// @brief Gets the head of the list.
|
||||
/// @return The head of the list, or null if the list is empty.
|
||||
constexpr T* GetHead() const { return _head ? LinkToItem(_head) : nullptr; }
|
||||
|
||||
/// @brief Gets the tail of the list.
|
||||
/// @return The tail of the list, or null if the list is empty.
|
||||
constexpr T* GetTail() const { return _tail ? LinkToItem(_tail) : nullptr; }
|
||||
|
||||
/// @brief Gets the item that comes after the given item.
|
||||
/// @param item The item to get the next for.
|
||||
/// @return The next item, or null if item is the last item in the list.
|
||||
constexpr T* GetNext(T* item) const
|
||||
{
|
||||
LinkedListLink* itemLink = &(item->*member);
|
||||
return itemLink->next ? LinkToItem(itemLink->next) : nullptr;
|
||||
}
|
||||
|
||||
/// @brief Gets the item that comes before the given item.
|
||||
/// @param item The item to get the previous for.
|
||||
/// @return The previous item, or null if item is the first item in the list.
|
||||
constexpr T* GetPrevious(T* item) const
|
||||
{
|
||||
LinkedListLink* itemLink = &(item->*member);
|
||||
return itemLink->prev ? LinkToItem(itemLink->prev) : nullptr;
|
||||
}
|
||||
|
||||
/// @brief Gets the amount of items in the list.
|
||||
/// @return The amount of items in the list.
|
||||
constexpr u32 GetCount() const { return _count; }
|
||||
|
||||
/// @brief Gets an iterator to the start of the list.
|
||||
/// @return An iterator to start of the list.
|
||||
constexpr Iterator begin() const { return Iterator(_head); }
|
||||
|
||||
/// @brief Gets an iterator to the end of the list.
|
||||
/// @return An iterator to end of the list.
|
||||
constexpr Iterator end() const { return Iterator(nullptr); }
|
||||
|
||||
private:
|
||||
LinkedListLink* _head = nullptr;
|
||||
LinkedListLink* _tail = nullptr;
|
||||
u32 _count = 0;
|
||||
|
||||
/// @brief Converts a list link pointer to an item pointer.
|
||||
/// @param link The list link pointer.
|
||||
/// @return The item pointer.
|
||||
static constexpr T* LinkToItem(LinkedListLink* link)
|
||||
{
|
||||
return (T*)(((u8*)link)-((size_t)&(((T*)nullptr)->*member)));
|
||||
}
|
||||
};
|
||||
11
arm9/source/core/LinkedListLink.h
Normal file
11
arm9/source/core/LinkedListLink.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Link for a linked list.
|
||||
struct LinkedListLink
|
||||
{
|
||||
/// @brief Pointer to the next list link in the list.
|
||||
LinkedListLink* next = nullptr;
|
||||
|
||||
/// @brief Pointer to the previous list link in the list.
|
||||
LinkedListLink* prev = nullptr;
|
||||
};
|
||||
163
arm9/source/core/SharedPtr.h
Normal file
163
arm9/source/core/SharedPtr.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
static inline u32 arm_getCpsr()
|
||||
{
|
||||
u32 cpsr;
|
||||
asm volatile("mrs %0, cpsr" : "=r" (cpsr));
|
||||
return cpsr;
|
||||
}
|
||||
|
||||
static inline void arm_setCpsrControl(u32 cpsrControl)
|
||||
{
|
||||
asm volatile("msr cpsr_c, %0" :: "r" (cpsrControl) : "cc");
|
||||
}
|
||||
|
||||
static inline u32 arm_disableIrqs(void)
|
||||
{
|
||||
u32 oldCpsr = arm_getCpsr();
|
||||
arm_setCpsrControl(oldCpsr | 0x80);
|
||||
return oldCpsr;
|
||||
}
|
||||
|
||||
static inline void arm_restoreIrqs(u32 oldCpsr)
|
||||
{
|
||||
arm_setCpsrControl(oldCpsr);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class SharedPtr
|
||||
{
|
||||
T* _pointer;
|
||||
vu32* _refCount;
|
||||
|
||||
public:
|
||||
SharedPtr()
|
||||
: _pointer(nullptr), _refCount(nullptr) { (void)sizeof(T); }
|
||||
|
||||
explicit SharedPtr(T* pointer)
|
||||
: _pointer(pointer), _refCount(pointer ? new u32(1) : nullptr) { (void)sizeof(T); }
|
||||
|
||||
SharedPtr(const SharedPtr& other)
|
||||
{
|
||||
u32 irq = arm_disableIrqs();
|
||||
_pointer = other._pointer;
|
||||
_refCount = other._refCount;
|
||||
if (_pointer)
|
||||
{
|
||||
(*_refCount)++;
|
||||
}
|
||||
arm_restoreIrqs(irq);
|
||||
}
|
||||
|
||||
SharedPtr(SharedPtr&& other)
|
||||
: _pointer(other._pointer), _refCount(other._refCount)
|
||||
{
|
||||
other._pointer = nullptr;
|
||||
other._refCount = nullptr;
|
||||
}
|
||||
|
||||
~SharedPtr()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
SharedPtr& operator=(const SharedPtr& other)
|
||||
{
|
||||
u32 irq = arm_disableIrqs();
|
||||
T* pointer = _pointer;
|
||||
if (pointer)
|
||||
{
|
||||
vu32* refCount = _refCount;
|
||||
u32 newValue = *refCount - 1;
|
||||
*refCount = newValue;
|
||||
_pointer = other._pointer;
|
||||
_refCount = other._refCount;
|
||||
if (_pointer)
|
||||
{
|
||||
(*_refCount)++;
|
||||
}
|
||||
arm_restoreIrqs(irq);
|
||||
if (newValue == 0)
|
||||
{
|
||||
delete pointer;
|
||||
delete refCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_pointer = other._pointer;
|
||||
_refCount = other._refCount;
|
||||
if (_pointer)
|
||||
{
|
||||
(*_refCount)++;
|
||||
}
|
||||
arm_restoreIrqs(irq);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
SharedPtr& operator=(SharedPtr&& other)
|
||||
{
|
||||
u32 irq = arm_disableIrqs();
|
||||
T* pointer = _pointer;
|
||||
if (pointer)
|
||||
{
|
||||
vu32* refCount = _refCount;
|
||||
u32 newValue = *refCount - 1;
|
||||
*refCount = newValue;
|
||||
_pointer = other._pointer;
|
||||
_refCount = other._refCount;
|
||||
other._pointer = nullptr;
|
||||
other._refCount = nullptr;
|
||||
arm_restoreIrqs(irq);
|
||||
if (newValue == 0)
|
||||
{
|
||||
delete pointer;
|
||||
delete refCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_pointer = other._pointer;
|
||||
_refCount = other._refCount;
|
||||
other._pointer = nullptr;
|
||||
other._refCount = nullptr;
|
||||
arm_restoreIrqs(irq);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void Reset()
|
||||
{
|
||||
u32 irq = arm_disableIrqs();
|
||||
T* pointer = _pointer;
|
||||
if (pointer)
|
||||
{
|
||||
vu32* refCount = _refCount;
|
||||
u32 newValue = *refCount - 1;
|
||||
*refCount = newValue;
|
||||
_pointer = nullptr;
|
||||
_refCount = nullptr;
|
||||
arm_restoreIrqs(irq);
|
||||
if (newValue == 0)
|
||||
{
|
||||
delete pointer;
|
||||
delete refCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arm_restoreIrqs(irq);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T& operator*() const { return *_pointer; }
|
||||
constexpr T* operator->() const { return _pointer; }
|
||||
|
||||
constexpr T* GetPointer() const { return _pointer; }
|
||||
constexpr u32 GetRefCount() const { return _refCount ? *_refCount : 0; }
|
||||
constexpr bool IsValid() const { return _pointer; }
|
||||
};
|
||||
60
arm9/source/core/String.h
Normal file
60
arm9/source/core/String.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
#include "StringUtil.h"
|
||||
|
||||
template <typename CharType, int MaxLength>
|
||||
class String
|
||||
{
|
||||
CharType _buffer[MaxLength + 1];
|
||||
|
||||
public:
|
||||
String()
|
||||
{
|
||||
_buffer[0] = 0;
|
||||
}
|
||||
|
||||
template<class T, class = std::enable_if_t<std::is_same_v<T, CharType>>>
|
||||
String(const T* const & str)
|
||||
{
|
||||
StringUtil::Copy(_buffer, str, MaxLength + 1);
|
||||
}
|
||||
|
||||
explicit String(const char* str)
|
||||
{
|
||||
StringUtil::Copy(_buffer, str, MaxLength + 1);
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
constexpr String(const CharType(&str)[N])
|
||||
{
|
||||
if (N * sizeof(CharType) <= sizeof(_buffer))
|
||||
memcpy(_buffer, str, N * sizeof(CharType));
|
||||
else
|
||||
{
|
||||
memcpy(_buffer, str, MaxLength * sizeof(CharType));
|
||||
_buffer[MaxLength] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class = std::enable_if_t<std::is_same_v<T, CharType>>>
|
||||
void operator=(const T* const & str)
|
||||
{
|
||||
StringUtil::Copy(_buffer, str, MaxLength + 1);
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
constexpr void operator=(const CharType(&str)[N])
|
||||
{
|
||||
if (N * sizeof(CharType) <= sizeof(_buffer))
|
||||
memcpy(_buffer, str, N * sizeof(CharType));
|
||||
else
|
||||
{
|
||||
memcpy(_buffer, str, MaxLength * sizeof(CharType));
|
||||
_buffer[MaxLength] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr operator const CharType*() const { return _buffer; }
|
||||
|
||||
constexpr const CharType* GetString() const { return _buffer; }
|
||||
};
|
||||
104
arm9/source/core/StringUtil.cpp
Normal file
104
arm9/source/core/StringUtil.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include "StringUtil.h"
|
||||
|
||||
u32 StringUtil::Copy(char* dst, const char* src, u32 dstLength)
|
||||
{
|
||||
if (!dst || dstLength == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 i = 0;
|
||||
if (src)
|
||||
{
|
||||
for (; i < dstLength - 1; i++)
|
||||
{
|
||||
char c = src[i];
|
||||
dst[i] = c;
|
||||
if (c == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst[i] = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
u32 StringUtil::Copy(char16_t* dst, const char16_t* src, u32 dstLength)
|
||||
{
|
||||
if (!dst || dstLength == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 i = 0;
|
||||
if (src)
|
||||
{
|
||||
for (; i < dstLength - 1; i++)
|
||||
{
|
||||
char16_t c = src[i];
|
||||
dst[i] = c;
|
||||
if (c == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst[i] = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
u32 StringUtil::Copy(char16_t* dst, const char* src, u32 dstLength)
|
||||
{
|
||||
if (!dst || dstLength == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 i = 0;
|
||||
if (src)
|
||||
{
|
||||
for (; i < dstLength - 1; i++)
|
||||
{
|
||||
char c0 = *src++;
|
||||
|
||||
// decode UTF-8
|
||||
if ((c0 & 0x80) == 0)
|
||||
{
|
||||
// 1 byte
|
||||
dst[i] = c0 & 0x7F;
|
||||
}
|
||||
else if ((c0 & 0xE0) == 0xC0)
|
||||
{
|
||||
// 2 bytes
|
||||
char c1 = *src++;
|
||||
dst[i] = ((c0 & 0x1F) << 6) | (c1 & 0x3F);
|
||||
}
|
||||
else if ((c0 & 0xF0) == 0xE0)
|
||||
{
|
||||
// 3 bytes
|
||||
char c1 = *src++;
|
||||
char c2 = *src++;
|
||||
dst[i] = ((c0 & 0x0F) << 12) | ((c1 & 0x3F) << 6) | (c2 & 0x3F);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 4 bytes; not supported
|
||||
src += 3;
|
||||
dst[i] = '?'; // substitute with question mark
|
||||
}
|
||||
|
||||
if (c0 == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst[i] = 0;
|
||||
return i;
|
||||
}
|
||||
32
arm9/source/core/StringUtil.h
Normal file
32
arm9/source/core/StringUtil.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
class StringUtil
|
||||
{
|
||||
public:
|
||||
/// @brief Copies the null terminated string in src to the
|
||||
/// dst buffer of size dstSize. If dstSize > 0 the dst
|
||||
/// buffer is guarenteed to be null terminated, even
|
||||
/// if src is truncated to fit in dst.
|
||||
/// @param dst The destination buffer.
|
||||
/// @param src The source string (null terminated).
|
||||
/// @param dstLength The length of the destination buffer in characters.
|
||||
static u32 Copy(char* dst, const char* src, u32 dstLength);
|
||||
|
||||
/// @brief Copies the null terminated string in src to the
|
||||
/// dst buffer of size dstSize. If dstSize > 0 the dst
|
||||
/// buffer is guarenteed to be null terminated, even
|
||||
/// if src is truncated to fit in dst.
|
||||
/// @param dst The destination buffer.
|
||||
/// @param src The source string (null terminated).
|
||||
/// @param dstLength The length of the destination buffer in char16_t characters.
|
||||
static u32 Copy(char16_t* dst, const char16_t* src, u32 dstLength);
|
||||
|
||||
/// @brief Copies the null terminated string in src to the
|
||||
/// dst buffer of size dstSize. If dstSize > 0 the dst
|
||||
/// buffer is guarenteed to be null terminated, even
|
||||
/// if src is truncated to fit in dst.
|
||||
/// @param dst The destination buffer.
|
||||
/// @param src The source string (null terminated).
|
||||
/// @param dstLength The length of the destination buffer in char16_t characters.
|
||||
static u32 Copy(char16_t* dst, const char* src, u32 dstLength);
|
||||
};
|
||||
3362
arm9/source/core/di.h
Normal file
3362
arm9/source/core/di.h
Normal file
File diff suppressed because it is too large
Load Diff
1272
arm9/source/core/heap/tlsf.c
Normal file
1272
arm9/source/core/heap/tlsf.c
Normal file
File diff suppressed because it is too large
Load Diff
90
arm9/source/core/heap/tlsf.h
Normal file
90
arm9/source/core/heap/tlsf.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef INCLUDED_tlsf
|
||||
#define INCLUDED_tlsf
|
||||
|
||||
/*
|
||||
** Two Level Segregated Fit memory allocator, version 3.1.
|
||||
** Written by Matthew Conte
|
||||
** http://tlsf.baisoku.org
|
||||
**
|
||||
** Based on the original documentation by Miguel Masmano:
|
||||
** http://www.gii.upv.es/tlsf/main/docs
|
||||
**
|
||||
** This implementation was written to the specification
|
||||
** of the document, therefore no GPL restrictions apply.
|
||||
**
|
||||
** Copyright (c) 2006-2016, Matthew Conte
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of the copyright holder nor the
|
||||
** names of its contributors may be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY
|
||||
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* tlsf_t: a TLSF structure. Can contain 1 to N pools. */
|
||||
/* pool_t: a block of memory that TLSF can manage. */
|
||||
typedef void* tlsf_t;
|
||||
typedef void* pool_t;
|
||||
|
||||
/* Create/destroy a memory pool. */
|
||||
tlsf_t tlsf_create(void* mem);
|
||||
tlsf_t tlsf_create_with_pool(void* mem, size_t bytes);
|
||||
void tlsf_destroy(tlsf_t tlsf);
|
||||
pool_t tlsf_get_pool(tlsf_t tlsf);
|
||||
|
||||
/* Add/remove memory pools. */
|
||||
pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes);
|
||||
void tlsf_remove_pool(tlsf_t tlsf, pool_t pool);
|
||||
|
||||
/* malloc/memalign/realloc/free replacements. */
|
||||
void* tlsf_malloc(tlsf_t tlsf, size_t bytes);
|
||||
void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t bytes);
|
||||
void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size);
|
||||
void tlsf_free(tlsf_t tlsf, void* ptr);
|
||||
|
||||
/* Returns internal block size, not original request size */
|
||||
size_t tlsf_block_size(void* ptr);
|
||||
|
||||
/* Overheads/limits of internal structures. */
|
||||
size_t tlsf_size(void);
|
||||
size_t tlsf_align_size(void);
|
||||
size_t tlsf_block_size_min(void);
|
||||
size_t tlsf_block_size_max(void);
|
||||
size_t tlsf_pool_overhead(void);
|
||||
size_t tlsf_alloc_overhead(void);
|
||||
|
||||
/* Debugging. */
|
||||
typedef void (*tlsf_walker)(void* ptr, size_t size, int used, void* user);
|
||||
void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user);
|
||||
/* Returns nonzero if any internal consistency check fails. */
|
||||
int tlsf_check(tlsf_t tlsf);
|
||||
int tlsf_check_pool(pool_t pool);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
93
arm9/source/core/math/ColorConverter.h
Normal file
93
arm9/source/core/math/ColorConverter.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
#include "Rgb.h"
|
||||
|
||||
class Rgb8To5Table
|
||||
{
|
||||
u8 _table[256];
|
||||
|
||||
public:
|
||||
constexpr Rgb8To5Table()
|
||||
: _table()
|
||||
{
|
||||
for (u32 i = 0; i < 256; i++)
|
||||
{
|
||||
u32 value5 = (i * 63 + 255) / (255 * 2);
|
||||
if (value5 > 31)
|
||||
value5 = 31;
|
||||
_table[i] = value5;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u8 Rgb8ToRgb5(u32 rgb8) const { return _table[rgb8]; }
|
||||
};
|
||||
|
||||
class Rgb8To6Table
|
||||
{
|
||||
u8 _table[256];
|
||||
|
||||
public:
|
||||
constexpr Rgb8To6Table()
|
||||
: _table()
|
||||
{
|
||||
for (u32 i = 0; i < 256; i++)
|
||||
_table[i] = (i * 63 + 128) / 255;
|
||||
}
|
||||
|
||||
constexpr u8 Rgb8ToRgb6(u32 rgb8) const { return _table[rgb8]; }
|
||||
};
|
||||
|
||||
class ColorConverter
|
||||
{
|
||||
static constexpr Rgb8To5Table sRgb8To5Table { };
|
||||
static constexpr Rgb8To6Table sRgb8To6Table { };
|
||||
public:
|
||||
/// @brief Unpacks a 15 bit color with format xbbb bbgg gggr rrrr.
|
||||
/// @param color The packed color.
|
||||
/// @return The unpacked color.
|
||||
static Rgb<5, 5, 5> FromXBGR555(u16 color)
|
||||
{
|
||||
return Rgb<5, 5, 5>(color & 0x1F, (color >> 5) & 0x1F, (color >> 10) & 0x1F);
|
||||
}
|
||||
|
||||
/// @brief Packs a 15 bit color with format xbbb bbgg gggr rrrr.
|
||||
/// Note that the color components are not clamped or masked.
|
||||
/// @param color The color to pack.
|
||||
/// @return The packed color.
|
||||
static u16 ToXBGR555(const Rgb<5, 5, 5>& color)
|
||||
{
|
||||
return color.r | (color.g << 5) | (color.b << 10);
|
||||
}
|
||||
|
||||
/// @brief Unpacks a 16 bit color with format Gbbb bbgg gggr rrrr.
|
||||
/// With G an additional lsb of green forming a 6 bit value together with ggggg.
|
||||
/// @param color The packed color.
|
||||
/// @return The unpacked color.
|
||||
static Rgb<5, 6, 5> FromGBGR565(u16 color)
|
||||
{
|
||||
return Rgb<5, 6, 5>(color & 0x1F, ((color >> 4) & 0x3E) | (color >> 15), (color >> 10) & 0x1F);
|
||||
}
|
||||
|
||||
/// @brief Packs a 16 bit color with format Gbbb bbgg gggr rrrr.
|
||||
/// With G an additional lsb of green forming a 6 bit value together with ggggg.
|
||||
/// Note that the color components are not clamped or masked.
|
||||
/// @param color The color to pack.
|
||||
/// @return The packed color.
|
||||
static u16 ToGBGR565(const Rgb<5, 6, 5>& color)
|
||||
{
|
||||
return color.r | ((color.g >> 1) << 5) | (color.b << 10) | (color.g << 15);
|
||||
}
|
||||
|
||||
/// @brief Packs a 16 bit color with format Gbbb bbgg gggr rrrr.
|
||||
/// With G an additional lsb of green forming a 6 bit value together with ggggg.
|
||||
/// Note that the color components are not clamped or masked.
|
||||
/// @param color The color to pack.
|
||||
/// @return The packed color.
|
||||
static u16 ToGBGR565(const Rgb<8, 8, 8>& color)
|
||||
{
|
||||
u32 r = sRgb8To5Table.Rgb8ToRgb5(color.r);
|
||||
u32 g = sRgb8To6Table.Rgb8ToRgb6(color.g);
|
||||
u32 b = sRgb8To5Table.Rgb8ToRgb5(color.b);
|
||||
|
||||
return r | ((g >> 1) << 5) | (b << 10) | (g << 15);
|
||||
}
|
||||
};
|
||||
18
arm9/source/core/math/Point.h
Normal file
18
arm9/source/core/math/Point.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
struct Point
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
|
||||
constexpr Point()
|
||||
: x(0), y(0) { }
|
||||
|
||||
constexpr Point(int x, int y)
|
||||
: x(x), y(y) { }
|
||||
|
||||
constexpr s64 DistanceSquaredTo(const Point& other) const
|
||||
{
|
||||
return (s64)(other.x - x) * (other.x - x) + (s64)(other.y - y) * (other.y - y);
|
||||
}
|
||||
};
|
||||
74
arm9/source/core/math/Rectangle.h
Normal file
74
arm9/source/core/math/Rectangle.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include "Point.h"
|
||||
|
||||
/// @brief Class representing a 2d rectangle.
|
||||
class Rectangle
|
||||
{
|
||||
int _x;
|
||||
int _y;
|
||||
int _width;
|
||||
int _height;
|
||||
|
||||
public:
|
||||
constexpr Rectangle(const Point& point, int width, int height)
|
||||
: _x(point.x), _y(point.y), _width(width), _height(height) { }
|
||||
|
||||
constexpr Rectangle(const Point& topLeft, const Point& bottomRight)
|
||||
: _x(topLeft.x), _y(topLeft.y)
|
||||
, _width(bottomRight.x - topLeft.x), _height(bottomRight.y - topLeft.y) { }
|
||||
|
||||
constexpr Rectangle(int x, int y, int width, int height)
|
||||
: _x(x), _y(y), _width(width), _height(height) { }
|
||||
|
||||
constexpr int GetX() const { return _x; }
|
||||
constexpr int GetY() const { return _y; }
|
||||
constexpr int GetWidth() const { return _width; }
|
||||
constexpr int GetHeight() const { return _height; }
|
||||
|
||||
constexpr int GetLeft() const { return _x; }
|
||||
constexpr int GetRight() const { return _x + _width; }
|
||||
constexpr int GetTop() const { return _y; }
|
||||
constexpr int GetBottom() const { return _y + _height; }
|
||||
|
||||
constexpr Point GetTopLeft() const { return Point(_x, _y); }
|
||||
constexpr Point GetBottomRight() const { return Point(_x + _width, _y + _height); }
|
||||
|
||||
constexpr Point GetTopCenter() const { return Point(_x + (_width >> 1), _y); }
|
||||
constexpr Point GetCenter() const { return Point(_x + (_width >> 1), _y + (_height >> 1)); }
|
||||
constexpr Point GetBottomCenter() const { return Point(_x + (_width >> 1), _y); }
|
||||
|
||||
/// @brief Checks if the given rectangle overlaps with this rectangle.
|
||||
/// @param other The rectangle to check.
|
||||
/// @return True if the given rectangle overlaps with this rectangle,
|
||||
/// or false otherwise.
|
||||
constexpr bool OverlapsWith(const Rectangle& other) const
|
||||
{
|
||||
return GetLeft() < other.GetRight() && other.GetLeft() < GetRight()
|
||||
&& GetTop() < other.GetBottom() && other.GetTop() < GetBottom();
|
||||
}
|
||||
|
||||
/// @brief Checks if the given point is contained in this rectangle.
|
||||
/// @param point The point to check.
|
||||
/// @return True if the given point is contained in this rectangle,
|
||||
/// or false otherwise.
|
||||
constexpr bool Contains(const Point& point) const
|
||||
{
|
||||
return point.x >= GetLeft() && point.x < GetRight()
|
||||
&& point.y >= GetTop() && point.y < GetBottom();
|
||||
}
|
||||
|
||||
/// @brief Checks if the given rectangle is fully contained in this rectangle.
|
||||
/// @param other The rectangle to check.
|
||||
/// @return True if the given rectangle is fully contained in this rectangle,
|
||||
/// or false otherwise.
|
||||
constexpr bool Contains(const Rectangle& other) const
|
||||
{
|
||||
return other.GetLeft() >= GetLeft() && other.GetRight() <= GetRight()
|
||||
&& other.GetTop() >= GetTop() && other.GetBottom() <= GetBottom();
|
||||
}
|
||||
|
||||
constexpr Rectangle OffsetBy(const Point& point) const
|
||||
{
|
||||
return Rectangle(_x + point.x, _y + point.y, _width, _height);
|
||||
}
|
||||
};
|
||||
77
arm9/source/core/math/Rgb.h
Normal file
77
arm9/source/core/math/Rgb.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include "common.h"
|
||||
|
||||
template <u32 RBits, u32 GBits, u32 BBits>
|
||||
class Rgb
|
||||
{
|
||||
public:
|
||||
u8 r, g, b;
|
||||
|
||||
constexpr Rgb()
|
||||
: r(0), g(0), b(0) { }
|
||||
|
||||
constexpr Rgb(u32 red, u32 green, u32 blue)
|
||||
: r(red), g(green), b(blue) { }
|
||||
|
||||
constexpr Rgb(const Rgb<RBits, GBits, BBits>& rgb)
|
||||
: r(rgb.r), g(rgb.g), b(rgb.b) { }
|
||||
|
||||
template <u32 RBits2, u32 GBits2, u32 BBits2>
|
||||
explicit constexpr Rgb(const Rgb<RBits2, GBits2, BBits2>& rgb)
|
||||
// {
|
||||
// if (RBits2 > RBits)
|
||||
// r = rgb.r >> (RBits2 - RBits);
|
||||
// else if (RBits2 < RBits)
|
||||
// r = rgb.r << (RBits - RBits2);
|
||||
// else
|
||||
// r = rgb.r;
|
||||
|
||||
// if (GBits2 > GBits)
|
||||
// g = rgb.g >> (GBits2 - GBits);
|
||||
// else if (GBits2 < GBits)
|
||||
// g = rgb.g << (GBits - GBits2);
|
||||
// else
|
||||
// g = rgb.g;
|
||||
|
||||
// if (BBits2 > BBits)
|
||||
// b = rgb.b >> (BBits2 - BBits);
|
||||
// else if (BBits2 < BBits)
|
||||
// b = rgb.b << (BBits - BBits2);
|
||||
// else
|
||||
// b = rgb.b;
|
||||
// }
|
||||
: r((rgb.r * ((1 << RBits) - 1) + ((1 << RBits2) >> 1)) / ((1 << RBits2) - 1))
|
||||
, g((rgb.g * ((1 << GBits) - 1) + ((1 << GBits2) >> 1)) / ((1 << GBits2) - 1))
|
||||
, b((rgb.b * ((1 << BBits) - 1) + ((1 << BBits2) >> 1)) / ((1 << BBits2) - 1)) { }
|
||||
|
||||
/// @brief Returns this color with its components clamped to their allowed range.
|
||||
/// @return The clamped color.
|
||||
constexpr Rgb Clamped() const
|
||||
{
|
||||
return Rgb(
|
||||
std::clamp<u32>(r, 0, (1 << RBits) - 1),
|
||||
std::clamp<u32>(g, 0, (1 << GBits) - 1),
|
||||
std::clamp<u32>(b, 0, (1 << BBits) - 1));
|
||||
}
|
||||
|
||||
/// @brief Adds the \p other color and clamps the resulting rgb values to their allowed range.
|
||||
/// @param other The color to add.
|
||||
/// @return The result of adding the colors and clamping the result.
|
||||
constexpr Rgb operator+(const Rgb& other) const
|
||||
{
|
||||
return Rgb(
|
||||
std::clamp<u32>(r + other.r, 0, (1 << RBits) - 1),
|
||||
std::clamp<u32>(g + other.g, 0, (1 << GBits) - 1),
|
||||
std::clamp<u32>(b + other.b, 0, (1 << BBits) - 1));
|
||||
}
|
||||
|
||||
/// @brief Adds the \p other color and without clamping the resulting rgb values.
|
||||
/// Note that this can cause an overflow.
|
||||
/// @param other The color to add.
|
||||
/// @return The result of adding the colors.
|
||||
constexpr Rgb AddUnclamped(const Rgb& other) const
|
||||
{
|
||||
return Rgb(r + other.r, g + other.g, b + other.b);
|
||||
}
|
||||
};
|
||||
26
arm9/source/core/math/RgbMixer.h
Normal file
26
arm9/source/core/math/RgbMixer.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "Rgb.h"
|
||||
#include "fixed.h"
|
||||
#include "ColorConverter.h"
|
||||
|
||||
class RgbMixer
|
||||
{
|
||||
public:
|
||||
template <u32 RBits, u32 GBits, u32 BBits>
|
||||
static Rgb<RBits, GBits, BBits> Lerp(const Rgb<RBits, GBits, BBits>& from, const Rgb<RBits, GBits, BBits>& to, int t, int tMax)
|
||||
{
|
||||
return Rgb<RBits, GBits, BBits>(
|
||||
(u32)((from.r * (tMax - t) + to.r * t) + (tMax >> 1)) / tMax,
|
||||
(u32)((from.g * (tMax - t) + to.g * t) + (tMax >> 1)) / tMax,
|
||||
(u32)((from.b * (tMax - t) + to.b * t) + (tMax >> 1)) / tMax);
|
||||
}
|
||||
|
||||
static void MakeGradientPalette(u16* palette, const Rgb<8, 8, 8>& from, const Rgb<8, 8, 8>& to)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
auto newColor = Lerp(from, to, i, 15);
|
||||
palette[i] = ColorConverter::ToGBGR565(newColor);
|
||||
}
|
||||
}
|
||||
};
|
||||
29
arm9/source/core/math/SinTable.h
Normal file
29
arm9/source/core/math/SinTable.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <cmath>
|
||||
#include "fixed.h"
|
||||
|
||||
template<int N>
|
||||
class SinTable
|
||||
{
|
||||
struct sin_cos_t
|
||||
{
|
||||
fix16<14> sin = 0;
|
||||
fix16<14> cos = 0;
|
||||
};
|
||||
|
||||
sin_cos_t _table[N];
|
||||
|
||||
public:
|
||||
constexpr SinTable()
|
||||
: _table()
|
||||
{
|
||||
for (auto i = 0; i < N; i++)
|
||||
_table[i] = { sin(i * 2 * M_PI / N), cos(i * 2 * M_PI / N) };
|
||||
}
|
||||
|
||||
constexpr fix16<14> Sin(u16 angle) const { return _table[(angle * N) >> 16].sin; }
|
||||
constexpr fix16<14> Cos(u16 angle) const { return _table[(angle * N) >> 16].cos; }
|
||||
constexpr const sin_cos_t& SinCos(u16 angle) const { return _table[(angle * N) >> 16]; }
|
||||
};
|
||||
|
||||
constexpr SinTable<4096> gSinTable = SinTable<4096>();
|
||||
479
arm9/source/core/math/fixed.h
Normal file
479
arm9/source/core/math/fixed.h
Normal file
@@ -0,0 +1,479 @@
|
||||
#pragma once
|
||||
#include <libtwl/math/mathDiv.h>
|
||||
|
||||
template <typename T, u32 FractionBits>
|
||||
class fixed
|
||||
{
|
||||
protected:
|
||||
T _value;
|
||||
|
||||
constexpr fixed(T rawValue, int dummy)
|
||||
: _value(rawValue) { }
|
||||
|
||||
public:
|
||||
constexpr fixed() { }
|
||||
|
||||
template <typename T2>
|
||||
constexpr fixed(fixed<T2, FractionBits> value)
|
||||
: _value(value.GetRawValue()) { }
|
||||
|
||||
template <typename T2, u32 OtherFractionBits>
|
||||
explicit constexpr fixed(fixed<T2, OtherFractionBits> value)
|
||||
{
|
||||
if (FractionBits == OtherFractionBits)
|
||||
_value = value.GetRawValue();
|
||||
else if (OtherFractionBits < FractionBits)
|
||||
_value = value.GetRawValue() << (FractionBits - OtherFractionBits);
|
||||
else
|
||||
{
|
||||
const T2 rounding = (T2)(1ULL << ((OtherFractionBits - FractionBits) - 1));
|
||||
const int shift = OtherFractionBits - FractionBits;
|
||||
_value = (value.GetRawValue() + rounding) >> shift;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr fixed(int value)
|
||||
: _value(value << FractionBits) { }
|
||||
|
||||
constexpr fixed(s64 value)
|
||||
: _value(value << FractionBits) { }
|
||||
|
||||
constexpr fixed(float value)
|
||||
: _value(std::round(value * (1 << FractionBits))) { }
|
||||
|
||||
constexpr fixed(double value)
|
||||
: _value(std::round(value * (1 << FractionBits))) { }
|
||||
|
||||
constexpr fixed(long double value)
|
||||
: _value(std::round(value * (1 << FractionBits))) { }
|
||||
|
||||
constexpr T Int() const
|
||||
{
|
||||
return _value >> FractionBits;
|
||||
}
|
||||
|
||||
constexpr T GetRawValue() const { return _value; }
|
||||
};
|
||||
|
||||
template <u32 FractionBits>
|
||||
class fix32;
|
||||
|
||||
template <u32 FractionBits>
|
||||
class fix64;
|
||||
|
||||
template <u32 FractionBits>
|
||||
class fix16 : public fixed<s16, FractionBits>
|
||||
{
|
||||
constexpr fix16(s16 rawValue, int dummy)
|
||||
: fixed<s16, FractionBits>(rawValue, dummy) { }
|
||||
|
||||
public:
|
||||
constexpr fix16() { }
|
||||
|
||||
constexpr fix16(const fix16<FractionBits>& value)
|
||||
: fixed<s16, FractionBits>(value) { }
|
||||
|
||||
template <typename T2, u32 OtherFractionBits>
|
||||
explicit constexpr fix16(const fixed<T2, OtherFractionBits>& value)
|
||||
: fixed<s16, FractionBits>(value) { }
|
||||
|
||||
constexpr fix16(s16 value)
|
||||
: fixed<s16, FractionBits>(value) { }
|
||||
|
||||
constexpr fix16(int value)
|
||||
: fixed<s16, FractionBits>(value) { }
|
||||
|
||||
constexpr fix16(float value)
|
||||
: fixed<s16, FractionBits>(value) { }
|
||||
|
||||
constexpr fix16(double value)
|
||||
: fixed<s16, FractionBits>(value) { }
|
||||
|
||||
constexpr fix16(long double value)
|
||||
: fixed<s16, FractionBits>(value) { }
|
||||
|
||||
constexpr bool operator<(const fix16& other) const
|
||||
{
|
||||
return this->_value < other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(const fix16& other) const
|
||||
{
|
||||
return this->_value <= other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const fix16& other) const
|
||||
{
|
||||
return this->_value > other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(const fix16& other) const
|
||||
{
|
||||
return this->_value >= other._value;
|
||||
}
|
||||
|
||||
constexpr fix16 operator-() const
|
||||
{
|
||||
return FromRawValue(-this->_value);
|
||||
}
|
||||
|
||||
constexpr fix16 operator+(const fix16& other) const
|
||||
{
|
||||
return FromRawValue(this->_value + other._value);
|
||||
}
|
||||
|
||||
template <typename TOther>
|
||||
constexpr fix16 operator+(TOther other) const
|
||||
{
|
||||
return FromRawValue(this->_value + fix16(other)._value);
|
||||
}
|
||||
|
||||
template <typename TLhs>
|
||||
constexpr friend fix16 operator+(TLhs lhs, const fix16& rhs)
|
||||
{
|
||||
return FromRawValue(fix16(lhs)._value + rhs._value);
|
||||
}
|
||||
|
||||
constexpr fix16 operator-(const fix16& other) const
|
||||
{
|
||||
return FromRawValue(this->_value - other._value);
|
||||
}
|
||||
|
||||
template <typename TOther>
|
||||
constexpr fix16 operator-(TOther other) const
|
||||
{
|
||||
return FromRawValue(this->_value - fix16(other)._value);
|
||||
}
|
||||
|
||||
template <typename TLhs>
|
||||
constexpr friend fix16 operator-(TLhs lhs, fix16 rhs)
|
||||
{
|
||||
return FromRawValue(fix16(lhs)._value - rhs._value);
|
||||
}
|
||||
|
||||
constexpr fix16 operator<<(int rhs) const
|
||||
{
|
||||
return FromRawValue(this->_value << rhs);
|
||||
}
|
||||
|
||||
constexpr fix16 operator>>(int rhs) const
|
||||
{
|
||||
return FromRawValue(this->_value >> rhs);
|
||||
}
|
||||
|
||||
constexpr fix16 abs() const
|
||||
{
|
||||
return FromRawValue(std::abs(this->_value));
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix32<FractionBits + OtherFractionBits> LongMul(const fix16<OtherFractionBits>& other) const
|
||||
{
|
||||
return fix32<FractionBits + OtherFractionBits>::FromRawValue(this->_value * other.GetRawValue());
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix64<FractionBits + OtherFractionBits> LongMul(const fix32<OtherFractionBits>& other) const
|
||||
{
|
||||
return fix64<FractionBits + OtherFractionBits>::FromRawValue(this->_value * (s64)other._value);
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix16 operator*(const fix16<OtherFractionBits>& other) const
|
||||
{
|
||||
return fix16(LongMul(other));
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix16 operator*(const fix32<OtherFractionBits>& other) const
|
||||
{
|
||||
return fix16(LongMul(other));
|
||||
}
|
||||
|
||||
constexpr fix16 operator*(s16 other) const
|
||||
{
|
||||
return FromRawValue(this->_value * other);
|
||||
}
|
||||
|
||||
constexpr friend fix16 operator*(s16 lhs, const fix16& rhs)
|
||||
{
|
||||
return FromRawValue(lhs * rhs.GetRawValue());
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix16 operator/(const fix16<OtherFractionBits>& other) const
|
||||
{
|
||||
s32 lhs = (s32)this->_value << 16;
|
||||
s32 rhs = other.GetRawValue();
|
||||
s32 divResult;
|
||||
if (std::is_constant_evaluated())
|
||||
divResult = lhs / rhs;
|
||||
else
|
||||
divResult = math_div32(lhs, rhs);
|
||||
return fix16(fix32<FractionBits + 16 - OtherFractionBits>::FromRawValue(divResult));
|
||||
}
|
||||
|
||||
static constexpr fix16 FromRawValue(s16 value)
|
||||
{
|
||||
return fix16(value, 0);
|
||||
}
|
||||
};
|
||||
|
||||
template <u32 FractionBits>
|
||||
class fix32 : public fixed<int, FractionBits>
|
||||
{
|
||||
constexpr fix32(int rawValue, int dummy)
|
||||
: fixed<int, FractionBits>(rawValue, dummy) { }
|
||||
|
||||
public:
|
||||
constexpr fix32() { }
|
||||
|
||||
constexpr fix32(const fix16<FractionBits>& value)
|
||||
: fixed<int, FractionBits>(value) { }
|
||||
|
||||
constexpr fix32(const fix32<FractionBits>& value)
|
||||
: fixed<int, FractionBits>(value) { }
|
||||
|
||||
template <typename T2, u32 OtherFractionBits>
|
||||
explicit constexpr fix32(const fixed<T2, OtherFractionBits>& value)
|
||||
: fixed<int, FractionBits>(value) { }
|
||||
|
||||
constexpr fix32(int value)
|
||||
: fixed<int, FractionBits>(value) { }
|
||||
|
||||
constexpr fix32(float value)
|
||||
: fixed<int, FractionBits>(value) { }
|
||||
|
||||
constexpr fix32(double value)
|
||||
: fixed<int, FractionBits>(value) { }
|
||||
|
||||
constexpr fix32(long double value)
|
||||
: fixed<int, FractionBits>(value) { }
|
||||
|
||||
constexpr bool operator==(const fix32& other) const
|
||||
{
|
||||
return this->_value == other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const fix32& other) const
|
||||
{
|
||||
return this->_value != other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator<(const fix32& other) const
|
||||
{
|
||||
return this->_value < other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(const fix32& other) const
|
||||
{
|
||||
return this->_value <= other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const fix32& other) const
|
||||
{
|
||||
return this->_value > other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(const fix32& other) const
|
||||
{
|
||||
return this->_value >= other._value;
|
||||
}
|
||||
|
||||
constexpr fix32 operator-() const
|
||||
{
|
||||
return FromRawValue(-this->_value);
|
||||
}
|
||||
|
||||
constexpr fix32 operator+(const fix32& other) const
|
||||
{
|
||||
return FromRawValue(this->_value + other._value);
|
||||
}
|
||||
|
||||
template <typename TOther>
|
||||
constexpr fix32 operator+(TOther other) const
|
||||
{
|
||||
return FromRawValue(this->_value + fix32(other)._value);
|
||||
}
|
||||
|
||||
template <typename TLhs>
|
||||
constexpr friend fix32 operator+(TLhs lhs, const fix32& rhs)
|
||||
{
|
||||
return FromRawValue(fix32(lhs)._value + rhs._value);
|
||||
}
|
||||
|
||||
constexpr fix32 operator-(const fix32& other) const
|
||||
{
|
||||
return FromRawValue(this->_value - other._value);
|
||||
}
|
||||
|
||||
constexpr fix32 operator-(int other) const
|
||||
{
|
||||
return FromRawValue(this->_value - fix32(other)._value);
|
||||
}
|
||||
|
||||
constexpr friend fix32 operator-(int lhs, const fix32& rhs)
|
||||
{
|
||||
return FromRawValue(fix32(lhs)._value - rhs._value);
|
||||
}
|
||||
|
||||
constexpr fix32 operator/(int other) const
|
||||
{
|
||||
return FromRawValue(this->_value / other);
|
||||
}
|
||||
|
||||
constexpr fix32 operator<<(int rhs) const
|
||||
{
|
||||
return FromRawValue(this->_value << rhs);
|
||||
}
|
||||
|
||||
constexpr fix32 operator>>(int rhs) const
|
||||
{
|
||||
return FromRawValue(this->_value >> rhs);
|
||||
}
|
||||
|
||||
constexpr fix32 Abs() const
|
||||
{
|
||||
return FromRawValue(std::abs(this->_value));
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix64<FractionBits + OtherFractionBits> LongMul(const fix16<OtherFractionBits>& other) const
|
||||
{
|
||||
return fix64<FractionBits + OtherFractionBits>::FromRawValue((s64)this->_value * other.GetRawValue());
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix64<FractionBits + OtherFractionBits> LongMul(const fix32<OtherFractionBits>& other) const
|
||||
{
|
||||
return fix64<FractionBits + OtherFractionBits>::FromRawValue((s64)this->_value * other.GetRawValue());
|
||||
}
|
||||
|
||||
constexpr fix64<FractionBits> LongMul(int other) const
|
||||
{
|
||||
return fix64<FractionBits>::FromRawValue((s64)this->_value * other);
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix32 operator*(const fix16<OtherFractionBits>& other) const
|
||||
{
|
||||
return fix32(LongMul(other));
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix32 operator*(const fix32<OtherFractionBits>& other) const
|
||||
{
|
||||
return fix32(LongMul(other));
|
||||
}
|
||||
|
||||
constexpr fix32 operator*(int other) const
|
||||
{
|
||||
return FromRawValue(this->_value * other);
|
||||
}
|
||||
|
||||
constexpr friend fix32 operator*(int lhs, const fix32& rhs)
|
||||
{
|
||||
return FromRawValue(lhs * rhs.GetRawValue());
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix32 operator/(const fix16<OtherFractionBits>& other) const
|
||||
{
|
||||
s64 lhs = (s64)this->_value << 32;
|
||||
s32 rhs = other.GetRawValue();
|
||||
s64 divResult;
|
||||
if (std::is_constant_evaluated())
|
||||
divResult = lhs / rhs;
|
||||
else
|
||||
divResult = math_div6432(lhs, rhs);
|
||||
return fix32(fix64<FractionBits + 32 - OtherFractionBits>::FromRawValue(divResult));
|
||||
}
|
||||
|
||||
template <u32 OtherFractionBits>
|
||||
constexpr fix32 operator/(const fix32<OtherFractionBits>& other) const
|
||||
{
|
||||
s64 lhs = (s64)this->_value << 32;
|
||||
s32 rhs = other.GetRawValue();
|
||||
s64 divResult;
|
||||
if (std::is_constant_evaluated())
|
||||
divResult = lhs / rhs;
|
||||
else
|
||||
divResult = math_div6432(lhs, rhs);
|
||||
return fix32(fix64<FractionBits + 32 - OtherFractionBits>::FromRawValue(divResult));
|
||||
}
|
||||
|
||||
static constexpr fix32 FromRawValue(int value)
|
||||
{
|
||||
return fix32(value, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// static constexpr fix32<12> operator""fx(long double value) { return fix32<12>(value); }
|
||||
|
||||
template <u32 FractionBits>
|
||||
class fix64 : public fixed<s64, FractionBits>
|
||||
{
|
||||
constexpr fix64(s64 rawValue, int dummy)
|
||||
: fixed<s64, FractionBits>(rawValue, dummy) { }
|
||||
|
||||
public:
|
||||
constexpr fix64() { }
|
||||
|
||||
template <typename T2>
|
||||
constexpr fix64(const fixed<T2, FractionBits>& value)
|
||||
: fixed<s64, FractionBits>(value) { }
|
||||
|
||||
template <typename T2, u32 OtherFractionBits>
|
||||
explicit constexpr fix64(const fixed<T2, OtherFractionBits>& value)
|
||||
: fixed<s64, FractionBits>(value) { }
|
||||
|
||||
constexpr fix64(int value)
|
||||
: fixed<s64, FractionBits>(value) { }
|
||||
|
||||
constexpr fix64(s64 value)
|
||||
: fixed<s64, FractionBits>(value) { }
|
||||
|
||||
constexpr fix64(float value)
|
||||
: fixed<s64, FractionBits>(value) { }
|
||||
|
||||
constexpr fix64(double value)
|
||||
: fixed<s64, FractionBits>(value) { }
|
||||
|
||||
constexpr fix64(long double value)
|
||||
: fixed<s64, FractionBits>(value) { }
|
||||
|
||||
constexpr bool operator<(const fix64& other) const
|
||||
{
|
||||
return this->_value < other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator<=(const fix64& other) const
|
||||
{
|
||||
return this->_value <= other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const fix64& other) const
|
||||
{
|
||||
return this->_value > other._value;
|
||||
}
|
||||
|
||||
constexpr bool operator>=(const fix64& other) const
|
||||
{
|
||||
return this->_value >= other._value;
|
||||
}
|
||||
|
||||
constexpr fix64 operator+(const fix64& other) const
|
||||
{
|
||||
return FromRawValue(this->_value + other._value);
|
||||
}
|
||||
|
||||
constexpr fix64 operator-(const fix64& other) const
|
||||
{
|
||||
return FromRawValue(this->_value - other._value);
|
||||
}
|
||||
|
||||
static constexpr fix64 FromRawValue(s64 value)
|
||||
{
|
||||
return fix64(value, 0);
|
||||
}
|
||||
};
|
||||
137
arm9/source/core/semihosting.h
Normal file
137
arm9/source/core/semihosting.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
|
||||
#define SYS_CLOSE 0x02
|
||||
#define SYS_CLOCK 0x10
|
||||
#define SYS_ELAPSED 0x30
|
||||
#define SYS_ERRNO 0x13
|
||||
#define SYS_FLEN 0x0C
|
||||
#define SYS_GET_CMDLINE 0x15
|
||||
#define SYS_HEAPINFO 0x16
|
||||
#define SYS_ISERROR 0x08
|
||||
#define SYS_ISTTY 0x09
|
||||
#define SYS_OPEN 0x01
|
||||
#define SYS_READ 0x06
|
||||
#define SYS_READC 0x07
|
||||
#define SYS_REMOVE 0x0E
|
||||
#define SYS_SEEK 0x0A
|
||||
#define SYS_SYSTEM 0x12
|
||||
#define SYS_TICKFREQ 0x31
|
||||
#define SYS_TIME 0x11
|
||||
#define SYS_TMPNAM 0x0D
|
||||
#define SYS_WRITE 0x05
|
||||
#define SYS_WRITEC 0x03
|
||||
#define SYS_WRITE0 0x04
|
||||
|
||||
#define SH_OPEN_MODE_R 0
|
||||
#define SH_OPEN_MODE_RB 1
|
||||
#define SH_OPEN_MODE_R_PLUS 2
|
||||
#define SH_OPEN_MODE_R_PLUS_B 3
|
||||
#define SH_OPEN_MODE_W 4
|
||||
#define SH_OPEN_MODE_WB 5
|
||||
#define SH_OPEN_MODE_W_PLUS 6
|
||||
#define SH_OPEN_MODE_W_PLUS_B 7
|
||||
#define SH_OPEN_MODE_A 8
|
||||
#define SH_OPEN_MODE_AB 9
|
||||
#define SH_OPEN_MODE_A_PLUS 10
|
||||
#define SH_OPEN_MODE_A_PLUS_B 11
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* path;
|
||||
u32 mode;
|
||||
u32 pathLength;
|
||||
} semihosting_open_params_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int handle;
|
||||
void* buffer;
|
||||
u32 length;
|
||||
} semihosting_read_params_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int handle;
|
||||
const void* buffer;
|
||||
u32 length;
|
||||
} semihosting_write_params_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int handle;
|
||||
u32 position;
|
||||
} semihosting_seek_params_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int sh_callSemihostingFunction(int function, const void* parameters);
|
||||
|
||||
static inline void sh_writeChar(char c)
|
||||
{
|
||||
sh_callSemihostingFunction(SYS_WRITEC, &c);
|
||||
}
|
||||
|
||||
static inline void sh_writeString(const char* str)
|
||||
{
|
||||
sh_callSemihostingFunction(SYS_WRITE0, str);
|
||||
}
|
||||
|
||||
static inline int sh_openFile(const char* path, u32 mode)
|
||||
{
|
||||
semihosting_open_params_t params =
|
||||
{
|
||||
path,
|
||||
mode,
|
||||
strlen(path)
|
||||
};
|
||||
return sh_callSemihostingFunction(SYS_OPEN, ¶ms);
|
||||
}
|
||||
|
||||
static inline int sh_readFile(int handle, void* buffer, u32 length)
|
||||
{
|
||||
semihosting_read_params_t params =
|
||||
{
|
||||
handle,
|
||||
buffer,
|
||||
length
|
||||
};
|
||||
return sh_callSemihostingFunction(SYS_READ, ¶ms);
|
||||
}
|
||||
|
||||
static inline int sh_writeFile(int handle, const void* buffer, u32 length)
|
||||
{
|
||||
semihosting_write_params_t params =
|
||||
{
|
||||
handle,
|
||||
buffer,
|
||||
length
|
||||
};
|
||||
return sh_callSemihostingFunction(SYS_WRITE, ¶ms);
|
||||
}
|
||||
|
||||
static inline int sh_seekFile(int handle, u32 position)
|
||||
{
|
||||
semihosting_seek_params_t params =
|
||||
{
|
||||
handle,
|
||||
position
|
||||
};
|
||||
return sh_callSemihostingFunction(SYS_SEEK, ¶ms);
|
||||
}
|
||||
|
||||
static inline int sh_closeFile(int handle)
|
||||
{
|
||||
return sh_callSemihostingFunction(SYS_CLOSE, &handle);
|
||||
}
|
||||
|
||||
static inline int sh_getFileSize(int handle)
|
||||
{
|
||||
return sh_callSemihostingFunction(SYS_FLEN, &handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
7
arm9/source/core/semihosting.s
Normal file
7
arm9/source/core/semihosting.s
Normal file
@@ -0,0 +1,7 @@
|
||||
.section .itcm
|
||||
.arm
|
||||
|
||||
.global sh_callSemihostingFunction
|
||||
sh_callSemihostingFunction:
|
||||
swi 0x123456
|
||||
bx lr
|
||||
39
arm9/source/core/task/Task.cpp
Normal file
39
arm9/source/core/task/Task.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "common.h"
|
||||
#include "Task.h"
|
||||
|
||||
void TaskBase::Execute()
|
||||
{
|
||||
u32 irqs = rtos_disableIrqs();
|
||||
if (_state == TaskState::NotStarted)
|
||||
{
|
||||
_state = TaskState::Running;
|
||||
rtos_restoreIrqs(irqs);
|
||||
TaskState finalState = ExecuteDirect();
|
||||
SetFinalState(finalState);
|
||||
}
|
||||
else
|
||||
rtos_restoreIrqs(irqs);
|
||||
}
|
||||
|
||||
void TaskBase::SetFinalState(TaskState finalState)
|
||||
{
|
||||
u32 irqs = rtos_disableIrqs();
|
||||
if (_state == TaskState::Running)
|
||||
{
|
||||
_state = finalState;
|
||||
rtos_wakeupQueue(&_threadQueue);
|
||||
}
|
||||
rtos_restoreIrqs(irqs);
|
||||
}
|
||||
|
||||
bool TaskBase::Wait()
|
||||
{
|
||||
u32 irq = rtos_disableIrqs();
|
||||
|
||||
if (!IsCompleted())
|
||||
rtos_queueThread(rtos_getCurThread(), &_threadQueue);
|
||||
|
||||
rtos_restoreIrqs(irq);
|
||||
|
||||
return IsCompletedSuccessfully();
|
||||
}
|
||||
89
arm9/source/core/task/Task.h
Normal file
89
arm9/source/core/task/Task.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/rtos/rtosThread.h>
|
||||
#include "TaskResult.h"
|
||||
|
||||
class TaskBase
|
||||
{
|
||||
public:
|
||||
virtual ~TaskBase() { }
|
||||
|
||||
void Execute();
|
||||
|
||||
TaskState GetState() const { return _state; }
|
||||
|
||||
bool IsCompleted() const { return _state >= TaskState::Completed; }
|
||||
bool IsCanceled() const { return _state == TaskState::Canceled; }
|
||||
bool IsFailed() const { return _state == TaskState::Failed; }
|
||||
bool IsCompletedSuccessfully() const { return _state == TaskState::Completed; }
|
||||
|
||||
bool Wait();
|
||||
|
||||
bool GetDestroyWhenComplete() const { return _destroyWhenComplete; }
|
||||
void SetDestroyWhenComplete() { _destroyWhenComplete = true; }
|
||||
|
||||
void RequestCancel() { _cancelRequested = true; }
|
||||
bool IsCancelRequested() const { return _cancelRequested; }
|
||||
|
||||
protected:
|
||||
volatile u8 _cancelRequested = false;
|
||||
|
||||
virtual TaskState ExecuteDirect() = 0;
|
||||
|
||||
private:
|
||||
rtos_thread_queue_t _threadQueue = { NULL };
|
||||
volatile TaskState _state = TaskState::NotStarted;
|
||||
volatile u8 _destroyWhenComplete = false;
|
||||
|
||||
void SetFinalState(TaskState finalState);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Task : public TaskBase
|
||||
{
|
||||
public:
|
||||
const T& GetResult() const { return this->_result; }
|
||||
|
||||
protected:
|
||||
virtual TaskResult<T> ExecuteFunc() const = 0;
|
||||
|
||||
private:
|
||||
T _result;
|
||||
|
||||
TaskState ExecuteDirect()
|
||||
{
|
||||
auto result = ExecuteFunc();
|
||||
if (result.GetFinalState() == TaskState::Completed)
|
||||
this->_result = result.GetResult();
|
||||
return result.GetFinalState();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class Task<void> : public TaskBase
|
||||
{
|
||||
protected:
|
||||
virtual TaskResult<void> ExecuteFunc() const = 0;
|
||||
|
||||
private:
|
||||
TaskState ExecuteDirect() override
|
||||
{
|
||||
auto result = ExecuteFunc();
|
||||
return result.GetFinalState();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, typename FuncType>
|
||||
class FuncTask : public Task<T>
|
||||
{
|
||||
public:
|
||||
FuncTask(const FuncType& function)
|
||||
: _function(function) { }
|
||||
|
||||
private:
|
||||
const FuncType _function;
|
||||
|
||||
TaskResult<T> ExecuteFunc() const override { return _function((const volatile u8&)this->_cancelRequested); }
|
||||
};
|
||||
27
arm9/source/core/task/TaskFactory.h
Normal file
27
arm9/source/core/task/TaskFactory.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "Task.h"
|
||||
|
||||
class TaskFactory
|
||||
{
|
||||
template <typename T>
|
||||
static T TaskResultToResultType(TaskResult<T> arg);
|
||||
|
||||
public:
|
||||
template <typename FuncType>
|
||||
static auto Create(const FuncType& function)
|
||||
{
|
||||
return FuncTask<decltype(TaskResultToResultType(function())), FuncType>(function);
|
||||
}
|
||||
|
||||
template <typename FuncType>
|
||||
static auto CreateOnHeap(const FuncType& function) -> Task<decltype(TaskResultToResultType(function()))>*
|
||||
{
|
||||
return new FuncTask<decltype(TaskResultToResultType(function())), FuncType>(function);
|
||||
}
|
||||
|
||||
template <typename FuncType>
|
||||
static auto CreateOnHeapUnique(const FuncType& function) -> std::unique_ptr<Task<decltype(TaskResultToResultType(function()))>>
|
||||
{
|
||||
return std::make_unique<FuncTask<decltype(TaskResultToResultType(function())), FuncType>>(function);
|
||||
}
|
||||
};
|
||||
43
arm9/source/core/task/TaskQueue.cpp
Normal file
43
arm9/source/core/task/TaskQueue.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "common.h"
|
||||
#include "TaskQueue.h"
|
||||
|
||||
void TaskQueueBase::ThreadMain(TaskBase** queue, u32 queueLength)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_idle = false;
|
||||
u32 readPtr = _queueReadPtr;
|
||||
while (readPtr != _queueWritePtr)
|
||||
{
|
||||
TaskBase* task = queue[readPtr];
|
||||
if (readPtr == queueLength - 1)
|
||||
readPtr = 0;
|
||||
else
|
||||
readPtr++;
|
||||
_queueReadPtr = readPtr;
|
||||
if (!task)
|
||||
continue;
|
||||
task->Execute();
|
||||
if (task->GetDestroyWhenComplete())
|
||||
{
|
||||
// this will destroy the task
|
||||
ReturnOwnership(task);
|
||||
}
|
||||
}
|
||||
if (_endThreadWhenDone)
|
||||
return;
|
||||
_idle = true;
|
||||
rtos_waitEvent(&_event, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueTaskBase::Dispose()
|
||||
{
|
||||
if (_task)
|
||||
{
|
||||
TaskBase* task = _task;
|
||||
_task = nullptr;
|
||||
_taskQueue->ReturnOwnership(task);
|
||||
_taskQueue = nullptr;
|
||||
}
|
||||
}
|
||||
212
arm9/source/core/task/TaskQueue.h
Normal file
212
arm9/source/core/task/TaskQueue.h
Normal file
@@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/rtos/rtosEvent.h>
|
||||
#include <libtwl/rtos/rtosThread.h>
|
||||
#include "core/BitVector.h"
|
||||
#include "Task.h"
|
||||
|
||||
class TaskQueueBase;
|
||||
|
||||
class QueueTaskBase
|
||||
{
|
||||
public:
|
||||
// forbid copies
|
||||
QueueTaskBase(const QueueTaskBase&) = delete;
|
||||
QueueTaskBase& operator=(const QueueTaskBase&) = delete;
|
||||
|
||||
// move assignment
|
||||
QueueTaskBase& operator=(QueueTaskBase&& other)
|
||||
{
|
||||
_taskQueue = other._taskQueue;
|
||||
_task = other._task;
|
||||
other._taskQueue = nullptr;
|
||||
other._task = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~QueueTaskBase()
|
||||
{
|
||||
// extra check here for optimizing out the dispose call after move assignment
|
||||
if (_task)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
void Dispose();
|
||||
|
||||
void CancelTask()
|
||||
{
|
||||
if (_task)
|
||||
{
|
||||
_task->RequestCancel();
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsValid() const { return _task != nullptr; }
|
||||
|
||||
protected:
|
||||
TaskBase* _task;
|
||||
|
||||
QueueTaskBase(TaskBase* task, TaskQueueBase* taskQueue)
|
||||
: _task(task), _taskQueue(taskQueue) { }
|
||||
|
||||
private:
|
||||
TaskQueueBase* _taskQueue;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class QueueTask : public QueueTaskBase
|
||||
{
|
||||
public:
|
||||
QueueTask()
|
||||
: QueueTaskBase(nullptr, nullptr) { }
|
||||
|
||||
QueueTask(Task<T>* task, TaskQueueBase* taskQueue)
|
||||
: QueueTaskBase(task, taskQueue) { }
|
||||
|
||||
const Task<T>& GetTask() const { return (const Task<T>&)*_task; }
|
||||
};
|
||||
|
||||
class TaskQueueBase
|
||||
{
|
||||
protected:
|
||||
template <typename T>
|
||||
static T TaskResultToResultType(TaskResult<T> arg);
|
||||
|
||||
public:
|
||||
virtual void ReturnOwnership(TaskBase* task) = 0;
|
||||
|
||||
template <typename FuncType>
|
||||
[[gnu::noinline]]
|
||||
auto Enqueue(const FuncType& function) -> QueueTask<decltype(TaskResultToResultType(function(*new vu8())))>
|
||||
{
|
||||
using TaskType = FuncTask<decltype(TaskResultToResultType(function(*new vu8()))), FuncType>;
|
||||
// static_assert(sizeof(TaskType) <= MaxTaskSize, "Task is too big for this pool");
|
||||
void* slot = GetSlot();
|
||||
auto task = new (slot) TaskType(function);
|
||||
Enqueue(task);
|
||||
return QueueTask(task, this);
|
||||
}
|
||||
|
||||
protected:
|
||||
rtos_event_t _event;
|
||||
vu32 _queueReadPtr = 0;
|
||||
vu32 _queueWritePtr = 0;
|
||||
volatile bool _endThreadWhenDone = false;
|
||||
volatile bool _idle = true;
|
||||
|
||||
void ThreadMain(TaskBase** queue, u32 queueLength);
|
||||
|
||||
TaskQueueBase()
|
||||
{
|
||||
rtos_createEvent(&_event);
|
||||
}
|
||||
|
||||
virtual void* GetSlot() = 0;
|
||||
virtual void Enqueue(TaskBase* task) = 0;
|
||||
};
|
||||
|
||||
template <u32 QueueLength, u32 MaxTaskSize>
|
||||
class TaskQueue : public TaskQueueBase
|
||||
{
|
||||
public:
|
||||
using TaskQueueBase::Enqueue;
|
||||
|
||||
~TaskQueue()
|
||||
{
|
||||
StopThread();
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void ReturnOwnership(TaskBase* task) override
|
||||
{
|
||||
u32 irqs = rtos_disableIrqs();
|
||||
if (task->IsCompleted())
|
||||
{
|
||||
task->~TaskBase();
|
||||
u32 slot = ((u32)task - (u32)_taskPool) / ((MaxTaskSize + 3) & ~3);
|
||||
_poolOccupation.Set(slot, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
task->SetDestroyWhenComplete();
|
||||
}
|
||||
rtos_restoreIrqs(irqs);
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void StartThread(u8 threadPriority, u32* stack, u32 stackSize)
|
||||
{
|
||||
if (_threadStarted)
|
||||
return;
|
||||
_endThreadWhenDone = false;
|
||||
_threadStarted = true;
|
||||
rtos_createThread(&_thread, threadPriority, ThreadMain, this, stack, stackSize);
|
||||
rtos_wakeupThread(&_thread);
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void StopThread()
|
||||
{
|
||||
if (!_threadStarted)
|
||||
return;
|
||||
_endThreadWhenDone = true;
|
||||
rtos_signalEvent(&_event);
|
||||
rtos_joinThread(&_thread);
|
||||
_threadStarted = false;
|
||||
}
|
||||
|
||||
bool IsIdle() const
|
||||
{
|
||||
return _queueReadPtr == _queueWritePtr && _idle;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 _taskPool[QueueLength][(MaxTaskSize + 3) / 4];
|
||||
BitVector<QueueLength> _poolOccupation;
|
||||
TaskBase* _queue[QueueLength];
|
||||
rtos_thread_t _thread;
|
||||
bool _threadStarted = false;
|
||||
|
||||
[[gnu::noinline]]
|
||||
u32 AcquireSlot()
|
||||
{
|
||||
u32 slot;
|
||||
u32 irqs = rtos_disableIrqs();
|
||||
{
|
||||
slot = _poolOccupation.FindFirstZero();
|
||||
_poolOccupation.Set(slot, 1);
|
||||
}
|
||||
rtos_restoreIrqs(irqs);
|
||||
LOG_DEBUG("AcquireSlot: %d\n", slot);
|
||||
return slot;
|
||||
}
|
||||
|
||||
void* GetSlot() override
|
||||
{
|
||||
return _taskPool[AcquireSlot()];
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void Enqueue(TaskBase* task) override
|
||||
{
|
||||
u32 writePtr = _queueWritePtr;
|
||||
_queue[writePtr] = task;
|
||||
if (writePtr == QueueLength - 1)
|
||||
_queueWritePtr = 0;
|
||||
else
|
||||
_queueWritePtr = writePtr + 1;
|
||||
rtos_signalEvent(&_event);
|
||||
}
|
||||
|
||||
static void ThreadMain(void* arg)
|
||||
{
|
||||
((TaskQueue*)arg)->ThreadMain();
|
||||
}
|
||||
|
||||
void ThreadMain()
|
||||
{
|
||||
TaskQueueBase::ThreadMain(&_queue[0], QueueLength);
|
||||
}
|
||||
};
|
||||
39
arm9/source/core/task/TaskResult.h
Normal file
39
arm9/source/core/task/TaskResult.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "TaskState.h"
|
||||
|
||||
template <class T>
|
||||
class TaskResult
|
||||
{
|
||||
const TaskState _finalState;
|
||||
const T _result;
|
||||
|
||||
TaskResult(TaskState finalState)
|
||||
: _finalState(finalState) { }
|
||||
|
||||
TaskResult(const T& result)
|
||||
: _finalState(TaskState::Completed), _result(result) { }
|
||||
|
||||
public:
|
||||
static TaskResult<T> Failed() { return TaskResult<T>(TaskState::Failed); }
|
||||
static TaskResult<T> Canceled() { return TaskResult<T>(TaskState::Canceled); }
|
||||
static TaskResult<T> Completed(const T& result) { return TaskResult<T>(result); }
|
||||
|
||||
TaskState GetFinalState() const { return _finalState; }
|
||||
const T& GetResult() const { return _result; }
|
||||
};
|
||||
|
||||
template <>
|
||||
class TaskResult<void>
|
||||
{
|
||||
const TaskState _finalState;
|
||||
|
||||
TaskResult(TaskState finalState)
|
||||
: _finalState(finalState) { }
|
||||
|
||||
public:
|
||||
static TaskResult<void> Failed() { return TaskResult<void>(TaskState::Failed); }
|
||||
static TaskResult<void> Canceled() { return TaskResult<void>(TaskState::Canceled); }
|
||||
static TaskResult<void> Completed() { return TaskResult<void>(TaskState::Completed); }
|
||||
|
||||
TaskState GetFinalState() const { return _finalState; }
|
||||
};
|
||||
10
arm9/source/core/task/TaskState.h
Normal file
10
arm9/source/core/task/TaskState.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
enum class TaskState : u8
|
||||
{
|
||||
NotStarted,
|
||||
Running,
|
||||
Completed,
|
||||
Canceled,
|
||||
Failed
|
||||
};
|
||||
Reference in New Issue
Block a user