Initial commit

This commit is contained in:
Gericom
2025-11-22 17:21:45 +01:00
commit 5d6f67c612
517 changed files with 63025 additions and 0 deletions

View 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;
};

View 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;
}
}

View 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; }
};

View 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)));
}
};

View 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;
};

View 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
View 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; }
};

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

1272
arm9/source/core/heap/tlsf.c Normal file

File diff suppressed because it is too large Load Diff

View 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

View 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);
}
};

View 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);
}
};

View 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);
}
};

View 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);
}
};

View 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);
}
}
};

View 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>();

View 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);
}
};

View 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, &params);
}
static inline int sh_readFile(int handle, void* buffer, u32 length)
{
semihosting_read_params_t params =
{
handle,
buffer,
length
};
return sh_callSemihostingFunction(SYS_READ, &params);
}
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, &params);
}
static inline int sh_seekFile(int handle, u32 position)
{
semihosting_seek_params_t params =
{
handle,
position
};
return sh_callSemihostingFunction(SYS_SEEK, &params);
}
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

View File

@@ -0,0 +1,7 @@
.section .itcm
.arm
.global sh_callSemihostingFunction
sh_callSemihostingFunction:
swi 0x123456
bx lr

View 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();
}

View 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); }
};

View 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);
}
};

View 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;
}
}

View 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);
}
};

View 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; }
};

View File

@@ -0,0 +1,10 @@
#pragma once
enum class TaskState : u8
{
NotStarted,
Running,
Completed,
Canceled,
Failed
};