summaryrefslogtreecommitdiff
path: root/mm/src/boot/O2
diff options
context:
space:
mode:
authorEblo <7004497+Eblo@users.noreply.github.com>2025-09-12 22:19:05 -0400
committerEblo <7004497+Eblo@users.noreply.github.com>2025-09-12 22:19:05 -0400
commitb95d50557327e35618988e2611472be78d7e4dae (patch)
tree233856a91f50703e7c9c7bb6c9715e3ace635058 /mm/src/boot/O2
parentf3c53811a4550adde5158b362dbf31ca1fbf9eac (diff)
Revert "Organize `libc64` files (#1492)"
This reverts commit e47a7050faf345ab2f220bfce58effd2590b9e5e.
Diffstat (limited to 'mm/src/boot/O2')
-rw-r--r--mm/src/boot/O2/__osMalloc.c459
-rw-r--r--mm/src/boot/O2/gfxprint.c3
-rw-r--r--mm/src/boot/O2/loadfragment.c6
-rw-r--r--mm/src/boot/O2/loadfragment2.c4
-rw-r--r--mm/src/boot/O2/math64.c191
-rw-r--r--mm/src/boot/O2/printutils.c25
-rw-r--r--mm/src/boot/O2/rand.c90
-rw-r--r--mm/src/boot/O2/sleep.c27
-rw-r--r--mm/src/boot/O2/sprintf.c33
-rw-r--r--mm/src/boot/O2/system_heap.c10
-rw-r--r--mm/src/boot/O2/system_malloc.c52
11 files changed, 888 insertions, 12 deletions
diff --git a/mm/src/boot/O2/__osMalloc.c b/mm/src/boot/O2/__osMalloc.c
new file mode 100644
index 000000000..afd626252
--- /dev/null
+++ b/mm/src/boot/O2/__osMalloc.c
@@ -0,0 +1,459 @@
+#include "os_malloc.h"
+#include "libc/stdbool.h"
+#include "libc/stdint.h"
+#include "macros.h"
+#include "functions.h"
+#include <string.h>
+
+#define FILL_ALLOCBLOCK (1 << 0)
+#define FILL_FREEBLOCK (1 << 1)
+#define CHECK_FREE_BLOCK (1 << 2)
+
+#define NODE_MAGIC (0x7373)
+
+#define BLOCK_UNINIT_MAGIC (0xAB)
+#define BLOCK_UNINIT_MAGIC_32 (0xABABABAB)
+#define BLOCK_ALLOC_MAGIC (0xCD)
+#define BLOCK_ALLOC_MAGIC_32 (0xCDCDCDCD)
+#define BLOCK_FREE_MAGIC (0xEF)
+#define BLOCK_FREE_MAGIC_32 (0xEFEFEFEF)
+
+OSMesg sArenaLockMsg[1];
+
+void __osMallocAddHeap(Arena* arena, void* heap, size_t size);
+
+void ArenaImpl_LockInit(Arena* arena) {
+ osCreateMesgQueue(&arena->lock, sArenaLockMsg, ARRAY_COUNT(sArenaLockMsg));
+}
+
+void ArenaImpl_Lock(Arena* arena) {
+ // osSendMesg(&arena->lock, NULL, OS_MESG_BLOCK);
+}
+
+void ArenaImpl_Unlock(Arena* arena) {
+ // osRecvMesg(&arena->lock, NULL, OS_MESG_BLOCK);
+}
+
+ArenaNode* ArenaImpl_GetLastBlock(Arena* arena) {
+ ArenaNode* last;
+ ArenaNode* iter;
+
+ last = arena->head;
+
+ if (last != NULL) {
+ iter = last->next;
+ while (iter != NULL) {
+ last = iter;
+ iter = iter->next;
+ }
+ }
+ return last;
+}
+
+/**
+ * Initializes \p arena to manage the memory region \p heap.
+ *
+ * @param arena The Arena to initialize.
+ * @param heap The memory region to use as heap space.
+ * @param size The size of the heap.
+ */
+void __osMallocInit(Arena* arena, void* heap, size_t size) {
+ memset(arena, 0, sizeof(Arena));
+
+ ArenaImpl_LockInit(arena);
+
+ __osMallocAddHeap(arena, heap, size);
+ arena->isInit = true;
+}
+
+// Original name: __osMallocAddBlock
+void __osMallocAddHeap(Arena* arena, void* heap, size_t size) {
+ ptrdiff_t diff;
+ s32 alignedSize;
+ ArenaNode* firstNode;
+ ArenaNode* lastNode;
+
+ if (heap == NULL) {
+ return;
+ }
+
+ firstNode = (ArenaNode*)ALIGN16((uintptr_t)heap);
+ diff = (uintptr_t)firstNode - (uintptr_t)heap;
+ alignedSize = ((s32)size - diff) & ~0xF;
+
+ // If the size of the heap is smaller than sizeof(ArenaNode), then the initialization will silently fail
+ if (alignedSize > (s32)sizeof(ArenaNode)) {
+ firstNode->next = NULL;
+ firstNode->prev = NULL;
+ firstNode->size = alignedSize - sizeof(ArenaNode);
+ firstNode->isFree = true;
+ firstNode->magic = NODE_MAGIC;
+
+ ArenaImpl_Lock(arena);
+
+ lastNode = ArenaImpl_GetLastBlock(arena);
+
+ // Checks if there's already a block
+ if (lastNode == NULL) {
+ arena->head = firstNode;
+ arena->start = heap;
+ } else {
+ // Chain the existing block with the new one
+ firstNode->prev = lastNode;
+ lastNode->next = firstNode;
+ }
+
+ ArenaImpl_Unlock(arena);
+ }
+}
+
+/**
+ * Clears the whole \p arena, invalidating every allocated pointer to it.
+ *
+ * @param arena The Arena to clear.
+ */
+void __osMallocCleanup(Arena* arena) {
+ memset(arena, 0, sizeof(Arena));
+}
+
+/**
+ * Returns whether or not the \p arena has been initialized.
+ *
+ * @param arena The Arena to check.
+ * @return u8 `true` if the \p arena has been initialized. `false` otherwise.
+ */
+u8 __osMallocIsInitalized(Arena* arena) {
+ return arena->isInit;
+}
+
+/**
+ * Allocates at least \p size bytes of memory using the given \p arena.
+ * The block of memory will be allocated at the start of the first sufficiently large free block.
+ *
+ * - If there's not enough space in the given \p arena, this function will fail, returning `NULL`.
+ * - If \p size is zero, then an empty region of memory is returned.
+ *
+ * To avoid memory leaks, the returned pointer should be eventually deallocated using either `__osFree` or
+ * `__osRealloc`.
+ *
+ * @param[in, out] arena The specific Arena to be used for the allocation.
+ * @param[in] size The size in bytes that will be allocated.
+ * @return void* On success, the allocated area of the \p arena memory. Otherwise, `NULL`.
+ */
+void* __osMalloc(Arena* arena, size_t size) {
+ ArenaNode* iter;
+ ArenaNode* newNode;
+ void* alloc = NULL;
+
+ size = ALIGN16(size);
+
+ ArenaImpl_Lock(arena);
+
+ // Start iterating from the head of the arena.
+ iter = arena->head;
+
+ // Iterate over the arena looking for a big enough space of memory.
+ while (iter != NULL) {
+ if (iter->isFree && iter->size >= size) {
+ size_t blockSize = ALIGN16(size) + sizeof(ArenaNode);
+
+ // If the block is larger than the requested size, then split it and just use the required size of the
+ // current block.
+ if (blockSize < iter->size) {
+ ArenaNode* next;
+
+ newNode = (ArenaNode*)((uintptr_t)iter + blockSize);
+ newNode->next = iter->next;
+ newNode->prev = iter;
+ newNode->size = iter->size - blockSize;
+ newNode->isFree = true;
+ newNode->magic = NODE_MAGIC;
+
+ iter->next = newNode;
+ iter->size = size;
+
+ next = newNode->next;
+ if (next != NULL) {
+ next->prev = newNode;
+ }
+ }
+
+ iter->isFree = false;
+ alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode));
+ break;
+ }
+
+ iter = iter->next;
+ }
+
+ ArenaImpl_Unlock(arena);
+
+ return alloc;
+}
+
+/**
+ * Allocates at least \p size bytes of memory using the given \p arena.
+ * Unlike __osMalloc, the block of memory will be allocated from the end of the \p arena.
+ *
+ * - If there's not enough space in the given \p arena, this function will fail, returning `NULL`.
+ * - If \p size is zero, then an empty region of memory is returned.
+ *
+ * To avoid memory leaks, the returned pointer should be eventually deallocated using `__osFree` or `__osRealloc`.
+ *
+ * @param[in, out] arena The specific Arena to be used for the allocation.
+ * @param[in] size The size in bytes that will be allocated.
+ * @return void* On success, the allocated area of the \p arena memory. Otherwise, `NULL`.
+ */
+void* __osMallocR(Arena* arena, size_t size) {
+ ArenaNode* iter;
+ ArenaNode* newNode;
+ size_t blockSize;
+ void* alloc = NULL;
+
+ size = ALIGN16(size);
+
+ ArenaImpl_Lock(arena);
+
+ // Start iterating from the last block of the arena.
+ iter = ArenaImpl_GetLastBlock(arena);
+
+ // Iterate in reverse the arena looking for a big enough space of memory.
+ while (iter != NULL) {
+ if (iter->isFree && iter->size >= size) {
+ blockSize = ALIGN16(size) + sizeof(ArenaNode);
+
+ // If the block is larger than the requested size, then split it and just use the required size of the
+ // current block.
+ if (blockSize < iter->size) {
+ ArenaNode* next;
+
+ newNode = (ArenaNode*)((uintptr_t)iter + (iter->size - size));
+ newNode->next = iter->next;
+ newNode->prev = iter;
+ newNode->size = size;
+ newNode->magic = NODE_MAGIC;
+
+ iter->next = newNode;
+ iter->size -= blockSize;
+
+ next = newNode->next;
+ if (next != NULL) {
+ next->prev = newNode;
+ }
+ iter = newNode;
+ }
+
+ iter->isFree = false;
+ alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode));
+ break;
+ }
+ iter = iter->prev;
+ }
+
+ ArenaImpl_Unlock(arena);
+
+ return alloc;
+}
+
+/**
+ * Deallocates the pointer \p ptr previously allocated by `__osMalloc`, `__osMallocR` or `__osRealloc`.
+ * If \p ptr is `NULL` or it has been already been freed, then this function does nothing.
+ *
+ * - The behaviour is undefined if \p ptr is not a memory region returned by one of the cited allocating
+ * functions.
+ * - The behaviour is undefined if \p ptr doesn't correspond to the given \p arena.
+ * - Any access to the freed pointer is undefined behaviour.
+ *
+ * @param[in, out] arena The specific Arena to be used for the allocation.
+ * @param[in, out] ptr The allocated memory block to deallocate.
+ */
+void __osFree(Arena* arena, void* ptr) {
+ ArenaNode* node;
+ ArenaNode* next;
+ ArenaNode* prev;
+
+ ArenaImpl_Lock(arena);
+
+ node = (ArenaNode*)((uintptr_t)ptr - sizeof(ArenaNode));
+
+ if ((ptr != NULL) && (node->magic == NODE_MAGIC) && !node->isFree) {
+ next = node->next;
+ prev = node->prev;
+ node->isFree = true;
+
+ // Checks if the next node is contiguous to the current node and if it isn't currently allocated. Then merge the
+ // two nodes into one.
+ if ((uintptr_t)next == (uintptr_t)node + sizeof(ArenaNode) + node->size && next->isFree) {
+ ArenaNode* newNext = next->next;
+
+ if (newNext != NULL) {
+ newNext->prev = node;
+ }
+
+ node->size += next->size + sizeof(ArenaNode);
+
+ node->next = newNext;
+ next = newNext;
+ }
+
+ // Checks if the previous node is contiguous to the current node and if it isn't currently allocated. Then merge
+ // the two nodes into one.
+ if ((prev != NULL) && prev->isFree && ((uintptr_t)node == (uintptr_t)prev + sizeof(ArenaNode) + prev->size)) {
+ if (next != NULL) {
+ next->prev = prev;
+ }
+
+ prev->next = next;
+ prev->size += node->size + sizeof(ArenaNode);
+ }
+ }
+
+ ArenaImpl_Unlock(arena);
+}
+
+/**
+ * Reallocates the pointer \p ptr.
+ * \p ptr must be either a pointer previously allocated by `__osMalloc`, `__osMallocR` or `__osRealloc` and
+ * not freed yet, or a `NULL` pointer.
+ *
+ * - If \p ptr is `NULL` a new pointer is allocated. See `__osMalloc` for more details.
+ * - If \p newSize is 0, then the given pointer is freed and `NULL` is returned. See `__osFree` for more details.
+ * - If \p newSize is bigger than the currently allocated allocated pointer, then the area of memory is expanded to a
+ * size big enough to fit the requested size.
+ *
+ * - The behaviour is undefined if \p ptr is not a memory region returned by one of the cited allocating
+ * functions.
+ * - The behaviour is undefined if \p ptr doesn't correspond to the given \p arena.
+ * - If the pointer is freed, then any access to the original freed pointer is undefined behaviour.
+ *
+ * @param[in, out] arena The specific Arena to be used for the allocation.
+ * @param[in, out] ptr The allocated memory block to deallocate.
+ * @param[in] newSize The new requested size.
+ * @return void* On success, the pointer to the reallocated area of memory. On failure, `NULL` is returned,
+ * and the original parameter \p ptr remains valid.
+ */
+void* __osRealloc(Arena* arena, void* ptr, size_t newSize) {
+ ArenaImpl_Lock(arena);
+
+ (void)"__osRealloc(%08x, %d)\n";
+
+ if (ptr == NULL) {
+ // if the `ptr` is NULL, then allocate a new pointer with the specified size
+ // if newSize is 0, then __osMalloc would return a NULL pointer
+ ptr = __osMalloc(arena, newSize);
+ } else if (newSize == 0) {
+ // if the requested size is zero, then free the pointer
+ __osFree(arena, ptr);
+ ptr = NULL;
+ } else {
+ size_t diff;
+ void* newPtr;
+ // Gets the start of the ArenaNode pointer embedded
+ ArenaNode* node = (void*)((uintptr_t)ptr - sizeof(ArenaNode));
+
+ newSize = ALIGN16(newSize);
+
+ // Only reallocate the memory if the new size isn't smaller than the actual node size
+ if ((newSize != node->size) && (node->size < newSize)) {
+ ArenaNode* next = node->next;
+
+ diff = newSize - node->size;
+ // Checks if the next node is contiguous to the current allocated node and it has enough space to fit the
+ // new requested size
+ if (((uintptr_t)next == (uintptr_t)node + node->size + sizeof(ArenaNode)) && (next->isFree) &&
+ (next->size >= diff)) {
+ ArenaNode* next2 = next->next;
+
+ next->size = (next->size - diff);
+ if (next2 != NULL) {
+ // Update the previous element of the linked list
+ next2->prev = (void*)((uintptr_t)next + diff);
+ }
+
+ next2 = (void*)((uintptr_t)next + diff);
+ node->next = next2;
+ node->size = newSize;
+ __osMemcpy(next2, next, sizeof(ArenaNode));
+ } else {
+ // Create a new pointer and manually copy the data from the old pointer to the new one
+ newPtr = __osMalloc(arena, newSize);
+ if (newPtr != NULL) {
+ memcpy(ptr, newPtr, node->size);
+ __osFree(arena, ptr);
+ }
+ ptr = newPtr;
+ }
+ }
+ }
+
+ ArenaImpl_Unlock(arena);
+
+ return ptr;
+}
+
+/**
+ * Gets the size of the largest free block, the total free space and the total allocated space.
+ *
+ * @param[in, out] arena The Arena which will be used to get the values from.
+ * @param[out] outMaxFree The size of the largest free block.
+ * @param[out] outFree The total free space.
+ * @param[out] outAlloc The total allocated space.
+ */
+void __osGetSizes(Arena* arena, size_t* outMaxFree, size_t* outFree, size_t* outAlloc) {
+ ArenaNode* iter;
+
+ ArenaImpl_Lock(arena);
+
+ *outMaxFree = 0;
+ *outFree = 0;
+ *outAlloc = 0;
+
+ iter = arena->head;
+ while (iter != NULL) {
+ if (iter->isFree) {
+ *outFree += iter->size;
+ if (*outMaxFree < iter->size) {
+ *outMaxFree = iter->size;
+ }
+ } else {
+ *outAlloc += iter->size;
+ }
+
+ iter = iter->next;
+ }
+
+ ArenaImpl_Unlock(arena);
+}
+
+/**
+ * Checks the validity of every node of the \p arena.
+ *
+ * @param arena The Arena to check.
+ * @return s32 0 if every pointer is valid. 1 otherwise.
+ */
+s32 __osCheckArena(Arena* arena) {
+ ArenaNode* iter;
+ s32 err = 0;
+
+ ArenaImpl_Lock(arena);
+
+ // "Checking the contents of the arena..."
+ (void)"アリーナの内容をチェックしています... (%08x)\n";
+
+ for (iter = arena->head; iter != NULL; iter = iter->next) {
+ if (iter->magic != NODE_MAGIC) {
+ // "Oops!!"
+ (void)"おおっと!! (%08x %08x)\n";
+
+ err = 1;
+ break;
+ }
+ }
+
+ // "The arena still looks good"
+ (void)"アリーナはまだ、いけそうです\n";
+
+ ArenaImpl_Unlock(arena);
+
+ return err;
+}
diff --git a/mm/src/boot/O2/gfxprint.c b/mm/src/boot/O2/gfxprint.c
index 50360bb6b..5b0ea20d0 100644
--- a/mm/src/boot/O2/gfxprint.c
+++ b/mm/src/boot/O2/gfxprint.c
@@ -1,5 +1,4 @@
#include "global.h"
-#include "libc64/aprintf.h"
#include "2s2h/BenPort.h"
#include "align_asset_macro.h"
@@ -390,7 +389,7 @@ Gfx* GfxPrint_Close(GfxPrint* this) {
}
s32 GfxPrint_VPrintf(GfxPrint* this, const char* fmt, va_list args) {
- return vaprintf(&this->callback, fmt, args);
+ return PrintUtils_VPrintf(&this->callback, fmt, args);
}
s32 GfxPrint_Printf(GfxPrint* this, const char* fmt, ...) {
diff --git a/mm/src/boot/O2/loadfragment.c b/mm/src/boot/O2/loadfragment.c
index 83ea54539..b1ba39d65 100644
--- a/mm/src/boot/O2/loadfragment.c
+++ b/mm/src/boot/O2/loadfragment.c
@@ -11,7 +11,7 @@
*/
#include "global.h"
-#include "libc64/malloc.h"
+#include "system_malloc.h"
#include "loadfragment.h"
#include <string.h>
@@ -187,7 +187,7 @@ void* Fragment_AllocateAndLoad(uintptr_t vromStart, uintptr_t vromEnd, void* vra
if (gLoadLogSeverity >= 3) {}
- allocatedRamAddr = malloc_r(size);
+ allocatedRamAddr = SystemArena_MallocR(size);
end = (uintptr_t)allocatedRamAddr + size;
if (gLoadLogSeverity >= 3) {}
@@ -203,7 +203,7 @@ void* Fragment_AllocateAndLoad(uintptr_t vromStart, uintptr_t vromEnd, void* vra
allocatedBytes = ovlRelocs->bssSize + size;
- allocatedRamAddr = realloc(allocatedRamAddr, allocatedBytes);
+ allocatedRamAddr = SystemArena_Realloc(allocatedRamAddr, allocatedBytes);
if (gLoadLogSeverity >= 3) {}
diff --git a/mm/src/boot/O2/loadfragment2.c b/mm/src/boot/O2/loadfragment2.c
index 32febf4a8..cd7da84f2 100644
--- a/mm/src/boot/O2/loadfragment2.c
+++ b/mm/src/boot/O2/loadfragment2.c
@@ -7,7 +7,7 @@
* These are for specific fragment overlays with the .ovl file extension
*/
#include "global.h"
-#include "libc64/malloc.h"
+#include "system_malloc.h"
#include "loadfragment.h"
s32 gOverlayLogSeverity = 2;
@@ -170,7 +170,7 @@ size_t Overlay_Load(uintptr_t vromStart, uintptr_t vromEnd, void* ramStart, void
}
void* Overlay_AllocateAndLoad(uintptr_t vromStart, uintptr_t vromEnd, void* vramStart, void* vramEnd) {
- void* allocatedRamAddr = malloc_r((uintptr_t)vramEnd - (uintptr_t)vramStart);
+ void* allocatedRamAddr = SystemArena_MallocR((uintptr_t)vramEnd - (uintptr_t)vramStart);
if (allocatedRamAddr != NULL) {
Overlay_Load(vromStart, vromEnd, vramStart, vramEnd, allocatedRamAddr);
diff --git a/mm/src/boot/O2/math64.c b/mm/src/boot/O2/math64.c
new file mode 100644
index 000000000..746be8e01
--- /dev/null
+++ b/mm/src/boot/O2/math64.c
@@ -0,0 +1,191 @@
+/**
+ * MathF library
+ * Contains tangent function, wrappers for a number of the handwritten functions in fp, and a suite of arctangents
+ */
+#include "global.h"
+#include "fixed_point.h"
+
+s32 gUseAtanContFrac;
+
+/**
+ * Tangent function computed using libultra sinf and cosf
+ */
+f32 Math_FTanF(f32 x) {
+ return sinf(x) / cosf(x);
+}
+
+// Unused
+f32 Math_FFloorF(f32 x) {
+ return floorf(x);
+}
+
+// Unused
+f32 Math_FCeilF(f32 x) {
+ return ceilf(x);
+}
+
+// Unused
+f32 Math_FRoundF(f32 x) {
+ return roundf(x);
+}
+
+// Unused
+f32 Math_FTruncF(f32 x) {
+ return truncf(x);
+}
+
+f32 Math_FNearbyIntF(f32 x) {
+ return nearbyintf(x);
+}
+
+/**
+ * Arctangent approximation using a Maclaurin series [https://mathworld.wolfram.com/MaclaurinSeries.html]
+ * (one quadrant, i.e. |x| < 1)
+ */
+f32 Math_FAtanTaylorQF(f32 x) {
+ // Coefficients of Maclaurin series of arctangent
+ static const f32 coeffs[] = {
+ -1.0f / 3, +1.0f / 5, -1.0f / 7, +1.0f / 9, -1.0f / 11, +1.0f / 13, -1.0f / 15, +1.0f / 17, 0.0f,
+ };
+
+ f32 poly = x;
+ f32 sq = SQ(x);
+ f32 exp = x * sq;
+ const f32* c = coeffs;
+ f32 term;
+
+ // Calculate the series until adding more terms does not change the float
+ while (true) {
+ term = *c++ * exp;
+ if (poly + term == poly) {
+ break;
+ }
+ poly += term;
+ exp *= sq;
+ }
+
+ return poly;
+}
+
+/**
+ * Extends previous arctangent function to the rest of the real numbers.
+ * Uses the formulae arctan(x) = pi/2 - arctan(1/x)
+ * and arctan(x) = pi/4 - arctan( (1-x)/(1+x) )
+ * to extend the range in which the series computed by Math_FAtanTaylorQF is a good approximation
+ */
+f32 Math_FAtanTaylorF(f32 x) {
+ f32 t;
+ f32 q;
+
+ if (x > 0.0f) {
+ t = x;
+ } else if (x < 0.0f) {
+ t = -x;
+ } else if (x == 0.0f) {
+ return 0.0f;
+ } else {
+ return qNaN0x10000;
+ }
+
+ if (t <= M_SQRT2f - 1.0f) {
+ return Math_FAtanTaylorQF(x);
+ }
+
+ if (t >= M_SQRT2f + 1.0f) {
+ q = M_PIf / 2 - Math_FAtanTaylorQF(1.0f / t);
+ } else { // in the interval (\sqrt{2} - 1, \sqrt{2} + 1)
+ q = M_PIf / 4 - Math_FAtanTaylorQF((1.0f - t) / (1.0f + t));
+ }
+
+ if (x > 0.0f) {
+ return q;
+ } else {
+ return -q;
+ }
+}
+
+/**
+ * Arctangent approximation using a continued fraction
+ * Cf. https://en.wikipedia.org/wiki/Gauss%27s_continued_fraction#The_series_2F1_2 ,
+ * https://dlmf.nist.gov/4.25#E4
+ */
+f32 Math_FAtanContFracF(f32 x) {
+ s32 sector;
+ f32 z;
+ f32 conv;
+ f32 sq;
+ s32 i;
+
+ if (x >= -1.0f && x <= 1.0f) {
+ sector = 0;
+ } else if (x > 1.0f) {
+ sector = 1;
+ x = 1.0f / x;
+ } else if (x < -1.0f) {
+ sector = -1;
+ x = 1.0f / x;
+ } else {
+ return qNaN0x10000;
+ }
+
+ // Builds the continued fraction from the innermost fraction out
+ sq = SQ(x);
+ conv = 0.0f;
+ z = 8.0f;
+ for (i = 8; i != 0; i--) {
+ conv = SQ(z) * sq / (2.0f * z + 1.0f + conv);
+ z -= 1.0f;
+ }
+ conv = x / (1.0f + conv);
+
+ if (sector == 0) {
+ return conv;
+ } else if (sector > 0) {
+ return M_PIf / 2 - conv;
+ } else {
+ return -M_PIf / 2 - conv;
+ }
+}
+
+/**
+ * Single-argument arctangent, only used by the two-argument function.
+ * Nothing else sets the bss variable gUseAtanContFrac, so the Maclaurin series is always used
+ */
+f32 Math_FAtanF(f32 x) {
+ if (!gUseAtanContFrac) {
+ return Math_FAtanTaylorF(x);
+ } else {
+ return Math_FAtanContFracF(x);
+ }
+}
+
+/**
+ * Main two-argument arctangent function
+ */
+f32 Math_FAtan2F(f32 y, f32 x) {
+ if (x == 0.0f) {
+ if (y == 0.0f) {
+ return 0.0f;
+ } else if (y > 0.0f) {
+ return M_PIf / 2;
+ } else if (y < 0.0f) {
+ return -M_PIf / 2;
+ } else {
+ return qNaN0x10000;
+ }
+ } else if (x >= 0.0f) {
+ return Math_FAtanF(y / x);
+ } else if (y < 0.0f) {
+ return Math_FAtanF(y / x) - M_PIf;
+ } else {
+ return M_PIf - Math_FAtanF(-(y / x));
+ }
+}
+
+f32 Math_FAsinF(f32 x) {
+ return Math_FAtan2F(x, sqrtf(1.0f - SQ(x)));
+}
+
+f32 Math_FAcosF(f32 x) {
+ return M_PIf / 2 - Math_FAsinF(x);
+}
diff --git a/mm/src/boot/O2/printutils.c b/mm/src/boot/O2/printutils.c
new file mode 100644
index 000000000..82e16f4dd
--- /dev/null
+++ b/mm/src/boot/O2/printutils.c
@@ -0,0 +1,25 @@
+#include "global.h"
+#include "stdio.h"
+
+s32 _Printf(PrintCallback a, void* arg, const char* fmt, va_list ap) {
+ unsigned char buffer[4096];
+
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ a(arg, buffer, strlen(buffer));
+}
+
+s32 PrintUtils_VPrintf(PrintCallback* pfn, const char* fmt, va_list args) {
+ return _Printf(*pfn, pfn, fmt, args);
+}
+
+s32 PrintUtils_Printf(PrintCallback* pfn, const char* fmt, ...) {
+ s32 ret;
+ va_list args;
+ va_start(args, fmt);
+
+ ret = PrintUtils_VPrintf(pfn, fmt, args);
+
+ va_end(args);
+
+ return ret;
+} \ No newline at end of file
diff --git a/mm/src/boot/O2/rand.c b/mm/src/boot/O2/rand.c
new file mode 100644
index 000000000..152c8a0e7
--- /dev/null
+++ b/mm/src/boot/O2/rand.c
@@ -0,0 +1,90 @@
+#include "rand.h"
+
+//! The latest generated random number, used to generate the next number in the sequence.
+static u32 sRandInt = 1;
+
+//! Space to store a value to be re-interpreted as a float.
+//! This can't be static because it is used in z_kankyo.
+u32 gRandFloat;
+
+/**
+ * Generates the next pseudo-random integer.
+ */
+u32 Rand_Next(void) {
+ return sRandInt = (sRandInt * RAND_MULTIPLIER) + RAND_INCREMENT;
+}
+
+/**
+ * Seeds the internal pseudo-random number generator with a provided starting value.
+ */
+void Rand_Seed(u32 seed) {
+ sRandInt = seed;
+}
+
+/**
+ * Returns a pseudo-random float between 0.0f and 1.0f from the internal PRNG.
+ *
+ * @note Works by generating the next integer, masking it to an IEEE-754 compliant float between 1.0f and 2.0f, and
+ * subtracting 1.0f.
+ *
+ * @remark This is also recommended by Numerical Recipes, pp. 284-5.
+ */
+f32 Rand_ZeroOne(void) {
+ sRandInt = (sRandInt * RAND_MULTIPLIER) + RAND_INCREMENT;
+ gRandFloat = ((sRandInt >> 9) | 0x3F800000);
+ return *((f32*)&gRandFloat) - 1.0f;
+}
+
+/**
+ * Returns a pseudo-random float between -0.5f and 0.5f in the same way as Rand_ZeroOne().
+ */
+f32 Rand_Centered(void) {
+ sRandInt = (sRandInt * RAND_MULTIPLIER) + RAND_INCREMENT;
+ gRandFloat = ((sRandInt >> 9) | 0x3F800000);
+ return *((f32*)&gRandFloat) - 1.5f;
+}
+
+//! All functions below are unused variants of the above four, that use a provided random number variable instead of the
+//! internal `sRandInt`
+
+/**
+ * Seeds a provided pseudo-random number with a provided starting value.
+ *
+ * @see Rand_Seed
+ */
+void Rand_Seed_Variable(u32* rndNum, u32 seed) {
+ *rndNum = seed;
+}
+
+/**
+ * Generates the next pseudo-random number from the provided rndNum.
+ *
+ * @see Rand_Next
+ */
+u32 Rand_Next_Variable(u32* rndNum) {
+ return *rndNum = (*rndNum * RAND_MULTIPLIER) + RAND_INCREMENT;
+}
+
+/**
+ * Generates the next pseudo-random float between 0.0f and 1.0f from the provided rndNum.
+ *
+ * @see Rand_ZeroOne
+ */
+f32 Rand_ZeroOne_Variable(u32* rndNum) {
+ u32 next = (*rndNum * RAND_MULTIPLIER) + RAND_INCREMENT;
+
+ gRandFloat = ((*rndNum = next) >> 9) | 0x3F800000;
+ return *((f32*)&gRandFloat) - 1.0f;
+}
+
+/**
+ * Generates the next pseudo-random float between -0.5f and 0.5f from the provided rndNum.
+ *
+ * @see Rand_ZeroOne, Rand_Centered
+ */
+f32 Rand_Centered_Variable(u32* rndNum) {
+ u32 next = (*rndNum * RAND_MULTIPLIER) + RAND_INCREMENT;
+
+ gRandFloat = ((*rndNum = next) >> 9) | 0x3F800000;
+ return *((f32*)&gRandFloat) - 1.5f;
+}
diff --git a/mm/src/boot/O2/sleep.c b/mm/src/boot/O2/sleep.c
new file mode 100644
index 000000000..a45d7e7dd
--- /dev/null
+++ b/mm/src/boot/O2/sleep.c
@@ -0,0 +1,27 @@
+#include "global.h"
+
+void Sleep_Cycles(u64 time) {
+ // OSMesgQueue mq;
+ // OSMesg msg[1];
+ // OSTimer timer;
+
+ // osCreateMesgQueue(&mq, msg, ARRAY_COUNT(msg));
+ // osSetTimer(&timer, time, 0, &mq, NULL);
+ // osRecvMesg(&mq, NULL, OS_MESG_BLOCK);
+}
+
+void Sleep_Nsec(u32 nsec) {
+ // Sleep_Cycles(OS_NSEC_TO_CYCLES(nsec));
+}
+
+void Sleep_Usec(u32 usec) {
+ // Sleep_Cycles(OS_USEC_TO_CYCLES(usec));
+}
+
+void Sleep_Msec(u32 ms) {
+ // Sleep_Cycles((ms * OS_CPU_COUNTER) / 1000ULL);
+}
+
+void Sleep_Sec(u32 sec) {
+ // Sleep_Cycles(sec * OS_CPU_COUNTER);
+}
diff --git a/mm/src/boot/O2/sprintf.c b/mm/src/boot/O2/sprintf.c
new file mode 100644
index 000000000..bcb2a4384
--- /dev/null
+++ b/mm/src/boot/O2/sprintf.c
@@ -0,0 +1,33 @@
+#include "ultra64.h"
+#include "libc/stdlib.h"
+#include "libc/string.h"
+#include <stdio.h>
+
+#if 0
+void* proutSprintf(void* dst, const char* fmt, size_t size) {
+ return (void*)((uintptr_t)memcpy(dst, fmt, size) + size);
+}
+
+int vsprintf(char* dst, char* fmt, va_list args) {
+ int ans = _Printf(proutSprintf, dst, fmt, args);
+ if (ans > -1) {
+ dst[ans] = 0;
+ }
+ return ans;
+}
+
+int sprintf(char* dst, const char* fmt, ...) {
+ int ans;
+ va_list args;
+ va_start(args, fmt);
+
+ ans = _Printf(&proutSprintf, dst, fmt, args);
+ if (ans > -1) {
+ dst[ans] = 0;
+ }
+
+ va_end(args);
+
+ return ans;
+}
+#endif
diff --git a/mm/src/boot/O2/system_heap.c b/mm/src/boot/O2/system_heap.c
index 7ad21b6ae..c34129f12 100644
--- a/mm/src/boot/O2/system_heap.c
+++ b/mm/src/boot/O2/system_heap.c
@@ -2,11 +2,11 @@
* @file system_heap.c
*
* @note:
- * Only SystemHeap_Init() is used, and is essentially just a wrapper for MallocInit().
+ * Only SystemHeap_Init() is used, and is essentially just a wrapper for SystemArena_Init().
*
*/
#include "global.h"
-#include "libc64/malloc.h"
+#include "system_malloc.h"
typedef void (*BlockFunc)(uintptr_t);
typedef void (*BlockFunc1)(uintptr_t, u32);
@@ -31,12 +31,12 @@ void* SystemHeap_Malloc(size_t size) {
size = 1;
}
- return __osMalloc(&malloc_arena, size);
+ return __osMalloc(&gSystemArena, size);
}
void SystemHeap_Free(void* ptr) {
if (ptr != NULL) {
- __osFree(&malloc_arena, ptr);
+ __osFree(&gSystemArena, ptr);
}
}
@@ -118,6 +118,6 @@ void SystemHeap_RunInits(void) {
}
void SystemHeap_Init(void* start, size_t size) {
- MallocInit(start, size);
+ SystemArena_Init(start, size);
SystemHeap_RunInits();
}
diff --git a/mm/src/boot/O2/system_malloc.c b/mm/src/boot/O2/system_malloc.c
new file mode 100644
index 000000000..e41c11df9
--- /dev/null
+++ b/mm/src/boot/O2/system_malloc.c
@@ -0,0 +1,52 @@
+#include "global.h"
+#include "os_malloc.h"
+#include <string.h>
+
+Arena gSystemArena;
+
+void* SystemArena_Malloc(size_t size) {
+ return __osMalloc(&gSystemArena, size);
+}
+
+void* SystemArena_MallocR(size_t size) {
+ return __osMallocR(&gSystemArena, size);
+}
+
+void* SystemArena_Realloc(void* oldPtr, size_t newSize) {
+ return __osRealloc(&gSystemArena, oldPtr, newSize);
+}
+
+void SystemArena_Free(void* ptr) {
+ __osFree(&gSystemArena, ptr);
+}
+
+void* SystemArena_Calloc(size_t num, size_t size) {
+ void* ptr;
+ size_t totalSize = num * size;
+
+ ptr = __osMalloc(&gSystemArena, totalSize);
+ if (ptr != NULL) {
+ memset(ptr, 0, totalSize);
+ }
+ return ptr;
+}
+
+void SystemArena_GetSizes(size_t* maxFreeBlock, size_t* bytesFree, size_t* bytesAllocated) {
+ __osGetSizes(&gSystemArena, maxFreeBlock, bytesFree, bytesAllocated);
+}
+
+u32 SystemArena_CheckArena(void) {
+ return __osCheckArena(&gSystemArena);
+}
+
+void SystemArena_Init(void* start, size_t size) {
+ __osMallocInit(&gSystemArena, start, size);
+}
+
+void SystemArena_Cleanup(void) {
+ __osMallocCleanup(&gSystemArena);
+}
+
+u8 SystemArena_IsInitialized(void) {
+ return __osMallocIsInitalized(&gSystemArena);
+}