tuxanci2-sdk
SDK for tuxanci2
Log | Files | Refs | README | LICENSE
commit 55d60f0ee9bfaae07e1139d0888bb9b5e75206ce
Author: Connor Thomson <connor@gnufault.org>
Date: Sat, 31 Oct 2026 10:47:56 -0700
Initial commit
Diffstat:
21 files changed, 818 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,18 @@
+MIT License
+
+Copyright (c) 2026 Connor Thomson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
@@ -0,0 +1,2 @@
+# Tuxánci 2 SDK
+Software Development Kit for Tuxánci 2
diff --git a/include/stdbool.h b/include/stdbool.h
@@ -0,0 +1,10 @@
+#ifndef TA_STDBOOL_H
+#define TA_STDBOOL_H
+
+#define bool _Bool
+#define true 1
+#define false 0
+
+#define __bool_true_false_are_defined 1
+
+#endif // TA_STDBOOL_H
diff --git a/include/stddef.h b/include/stddef.h
@@ -0,0 +1,8 @@
+#ifndef TA_STDDEF_H
+#define TA_STDDEF_H
+
+#define NULL ((void*)0)
+
+typedef unsigned long size_t;
+
+#endif // TA_STDDEF_H
diff --git a/include/stdint.h b/include/stdint.h
@@ -0,0 +1,12 @@
+#ifndef TA_STDINT_H
+#define TA_STDINT_H
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+
+typedef signed char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+
+#endif // TA_STDINT_H
diff --git a/include/stdlib.h b/include/stdlib.h
@@ -0,0 +1,20 @@
+#ifndef TA_STDLIB_H
+#define TA_STDLIB_H
+
+#include <tuxanci2/syscall.h>
+#include <tuxanci2/abi.h>
+#include <stdint.h>
+#include <stddef.h>
+
+void *malloc(size_t size);
+void *calloc(size_t count, size_t size);
+void *realloc(void *ptr, size_t size);
+void free(void *ptr);
+
+static inline void exit(int status) {
+ ta_syscall(TA_SYS_EXIT, (uint32_t)status, 0, 0, 0, 0, 0);
+
+ while (1) {}
+}
+
+#endif // TA_STDLIB_H
diff --git a/include/string.h b/include/string.h
@@ -0,0 +1,11 @@
+#ifndef TA_STRING_H
+#define TA_STRING_H
+
+#include <stddef.h>
+
+void *memcpy(void *destination, const void *source, size_t count);
+void *memmove(void *destination, const void *source, size_t count);
+void *memset(void *destination, int value, size_t count);
+int memcmp(const void *first, const void *second, size_t count);
+
+#endif // TA_STRING_H
diff --git a/include/tuxanci2/abi.h b/include/tuxanci2/abi.h
@@ -0,0 +1,25 @@
+#ifndef TA_ABI_H
+#define TA_ABI_H
+
+#define TA_SYS_EXIT 0
+#define TA_SYS_READY 1
+#define TA_SYS_RETURN 2
+#define TA_SYS_DISPLAY_WIDTH 3
+#define TA_SYS_DISPLAY_HEIGHT 4
+#define TA_SYS_DISPLAY_SPRITE_MODE 5
+#define TA_SYS_KEYBOARD_IS_DOWN 10
+#define TA_SYS_IMAGE_INIT 11
+#define TA_SYS_IMAGE_DRAW 12
+#define TA_SYS_IMAGE_GET_SIZE 13
+#define TA_SYS_TEXT_CREATE 14
+#define TA_SYS_TEXT_WIDTH 15
+#define TA_SYS_TEXT_HEIGHT 16
+#define TA_SYS_TEXT_DRAW 17
+#define TA_SYS_TEXT_DESTROY 18
+#define TA_SYS_SCREEN_REGISTER 19
+#define TA_SYS_SCREEN_SWITCH 20
+
+#define TA_ASSET_BG 0
+#define TA_ASSET_LOADING_LOGO 1
+
+#endif // TA_ABI_H
diff --git a/include/tuxanci2/display.h b/include/tuxanci2/display.h
@@ -0,0 +1,23 @@
+#ifndef TA_DISPLAY_H
+#define TA_DISPLAY_H
+
+#include <tuxanci2/syscall.h>
+#include <tuxanci2/abi.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define TA_DISPLAY_RGB(red, green, blue) ((((uint32_t)(red) & 255u) << 16) | (((uint32_t)(green) & 255u) << 8) | ((uint32_t)(blue) & 255u))
+
+static inline uint32_t ta_display_width(void) {
+ return ta_syscall(TA_SYS_DISPLAY_WIDTH, 0, 0, 0, 0, 0, 0);
+}
+
+static inline uint32_t ta_display_height(void) {
+ return ta_syscall(TA_SYS_DISPLAY_HEIGHT, 0, 0, 0, 0, 0, 0);
+}
+
+static inline bool ta_display_sprite_mode(void) {
+ return ta_syscall(TA_SYS_DISPLAY_SPRITE_MODE, 0, 0, 0, 0, 0, 0) != 0;
+}
+
+#endif // TA_DISPLAY_H
diff --git a/include/tuxanci2/fixed.h b/include/tuxanci2/fixed.h
@@ -0,0 +1,10 @@
+#ifndef TA_FIXED_H
+#define TA_FIXED_H
+
+#include <stdint.h>
+
+typedef uint32_t ta_fixed;
+
+#define TA_FIXED(value) ((ta_fixed)((value) * 65536.0 + 0.5))
+
+#endif // TA_FIXED_H
diff --git a/include/tuxanci2/image.h b/include/tuxanci2/image.h
@@ -0,0 +1,23 @@
+#ifndef TA_IMAGE_H
+#define TA_IMAGE_H
+
+#include <tuxanci2/syscall.h>
+#include <tuxanci2/abi.h>
+#include <tuxanci2/fixed.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+static inline bool ta_image_init(uint32_t asset) {
+ return ta_syscall(TA_SYS_IMAGE_INIT, asset, 0, 0, 0, 0, 0) != 0;
+}
+
+static inline bool ta_image_draw(uint32_t asset, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, uint32_t frame) {
+ return ta_syscall(TA_SYS_IMAGE_DRAW, asset, x, y, width, height, frame) != 0;
+}
+
+static inline bool ta_image_get_size(uint32_t asset, uint32_t *width, uint32_t *height) {
+ return ta_syscall(TA_SYS_IMAGE_GET_SIZE, asset, (uint32_t)(size_t)width, (uint32_t)(size_t)height, 0, 0, 0) != 0;
+}
+
+#endif // TA_IMAGE_H
diff --git a/include/tuxanci2/keyboard.h b/include/tuxanci2/keyboard.h
@@ -0,0 +1,144 @@
+#ifndef TA_KEYBOARD_H
+#define TA_KEYBOARD_H
+
+#include <tuxanci2/display.h>
+#include <tuxanci2/abi.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef enum {
+ KEY_NONE = 0,
+ KEY_BACKSPACE = 8,
+ KEY_TAB = 9,
+ KEY_ENTER = 13,
+ KEY_ESCAPE = 27,
+ KEY_SPACE = 32,
+ KEY_EXCLAMATION_MARK = 33,
+ KEY_QUOTATION_MARK = 34,
+ KEY_HASH = 35,
+ KEY_DOLLAR_SIGN = 36,
+ KEY_PERCENT_SIGN = 37,
+ KEY_AMPERSAND = 38,
+ KEY_APOSTROPHE = 39,
+ KEY_LEFT_PARENTHESIS = 40,
+ KEY_RIGHT_PARENTHESIS = 41,
+ KEY_ASTERISK = 42,
+ KEY_PLUS = 43,
+ KEY_COMMA = 44,
+ KEY_MINUS = 45,
+ KEY_PERIOD = 46,
+ KEY_SLASH = 47,
+ KEY_0 = 48,
+ KEY_1 = 49,
+ KEY_2 = 50,
+ KEY_3 = 51,
+ KEY_4 = 52,
+ KEY_5 = 53,
+ KEY_6 = 54,
+ KEY_7 = 55,
+ KEY_8 = 56,
+ KEY_9 = 57,
+ KEY_COLON = 58,
+ KEY_SEMICOLON = 59,
+ KEY_LESS_THAN = 60,
+ KEY_EQUALS = 61,
+ KEY_GREATER_THAN = 62,
+ KEY_QUESTION_MARK = 63,
+ KEY_AT_SIGN = 64,
+ KEY_A = 97,
+ KEY_B = 98,
+ KEY_C = 99,
+ KEY_D = 100,
+ KEY_E = 101,
+ KEY_F = 102,
+ KEY_G = 103,
+ KEY_H = 104,
+ KEY_I = 105,
+ KEY_J = 106,
+ KEY_K = 107,
+ KEY_L = 108,
+ KEY_M = 109,
+ KEY_N = 110,
+ KEY_O = 111,
+ KEY_P = 112,
+ KEY_Q = 113,
+ KEY_R = 114,
+ KEY_S = 115,
+ KEY_T = 116,
+ KEY_U = 117,
+ KEY_V = 118,
+ KEY_W = 119,
+ KEY_X = 120,
+ KEY_Y = 121,
+ KEY_Z = 122,
+ KEY_LEFT_BRACKET = 91,
+ KEY_BACKSLASH = 92,
+ KEY_RIGHT_BRACKET = 93,
+ KEY_CARET = 94,
+ KEY_UNDERSCORE = 95,
+ KEY_GRAVE_ACCENT = 96,
+ KEY_LEFT_BRACE = 123,
+ KEY_PIPE = 124,
+ KEY_RIGHT_BRACE = 125,
+ KEY_TILDE = 126,
+ KEY_DELETE = 127,
+ KEY_UP = 128,
+ KEY_DOWN = 129,
+ KEY_LEFT = 130,
+ KEY_RIGHT = 131,
+ KEY_F1 = 132,
+ KEY_F2 = 133,
+ KEY_F3 = 134,
+ KEY_F4 = 135,
+ KEY_F5 = 136,
+ KEY_F6 = 137,
+ KEY_F7 = 138,
+ KEY_F8 = 139,
+ KEY_F9 = 140,
+ KEY_F10 = 141,
+ KEY_F11 = 142,
+ KEY_F12 = 143,
+ KEY_HOME = 144,
+ KEY_END = 145,
+ KEY_PAGE_UP = 146,
+ KEY_PAGE_DOWN = 147,
+ KEY_INSERT = 148,
+ KEY_CAPS_LOCK = 149,
+ KEY_NUM_LOCK = 150,
+ KEY_SCROLL_LOCK = 151,
+ KEY_PRINT_SCREEN = 152,
+ KEY_PAUSE = 153,
+ KEY_MENU = 154,
+ KEY_LEFT_SHIFT = 155,
+ KEY_RIGHT_SHIFT = 156,
+ KEY_LEFT_CTRL = 157,
+ KEY_RIGHT_CTRL = 158,
+ KEY_LEFT_ALT = 159,
+ KEY_RIGHT_ALT = 160,
+ KEY_LEFT_SUPER = 161,
+ KEY_RIGHT_SUPER = 162,
+ KEY_KP_0 = 163,
+ KEY_KP_1 = 164,
+ KEY_KP_2 = 165,
+ KEY_KP_3 = 166,
+ KEY_KP_4 = 167,
+ KEY_KP_5 = 168,
+ KEY_KP_6 = 169,
+ KEY_KP_7 = 170,
+ KEY_KP_8 = 171,
+ KEY_KP_9 = 172,
+ KEY_KP_DECIMAL = 173,
+ KEY_KP_DIVIDE = 174,
+ KEY_KP_MULTIPLY = 175,
+ KEY_KP_SUBTRACT = 176,
+ KEY_KP_ADD = 177,
+ KEY_KP_ENTER = 178,
+ KEY_KP_EQUALS = 179
+} ta_keyboard_keys;
+
+static inline bool ta_keyboard_is_down(ta_keyboard_keys key) {
+ return ta_syscall(TA_SYS_KEYBOARD_IS_DOWN, (uint32_t)key, 0, 0, 0, 0, 0) != 0;
+}
+
+
+#endif // TA_KEYBOARD_H
diff --git a/include/tuxanci2/screen.h b/include/tuxanci2/screen.h
@@ -0,0 +1,22 @@
+#ifndef TA_SCREEN_H
+#define TA_SCREEN_H
+
+#include <tuxanci2/syscall.h>
+#include <tuxanci2/abi.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+typedef void (*ta_screen_init_function)(void);
+typedef void (*ta_screen_update_function)(void);
+typedef void (*ta_screen_destroy_function)(void);
+
+static inline bool ta_screen_register(uint32_t id, ta_screen_init_function init, ta_screen_update_function update, ta_screen_destroy_function destroy) {
+ return ta_syscall(TA_SYS_SCREEN_REGISTER, id, (uint32_t)(size_t)init, (uint32_t)(size_t)update, (uint32_t)(size_t)destroy, 0, 0) != 0;
+}
+
+static inline bool ta_screen_switch(uint32_t id) {
+ return ta_syscall(TA_SYS_SCREEN_SWITCH, id, 0, 0, 0, 0, 0) != 0;
+}
+
+#endif // TA_SCREEN_H
diff --git a/include/tuxanci2/syscall.h b/include/tuxanci2/syscall.h
@@ -0,0 +1,20 @@
+#ifndef TA_SYSCALL_H
+#define TA_SYSCALL_H
+
+#include <stdint.h>
+
+static inline uint32_t ta_syscall(uint32_t number, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4, uint32_t arg5) {
+ register uint32_t reg0 __asm__("a0") = arg0;
+ register uint32_t reg1 __asm__("a1") = arg1;
+ register uint32_t reg2 __asm__("a2") = arg2;
+ register uint32_t reg3 __asm__("a3") = arg3;
+ register uint32_t reg4 __asm__("a4") = arg4;
+ register uint32_t reg5 __asm__("a5") = arg5;
+ register uint32_t reg7 __asm__("a7") = number;
+
+ __asm__ volatile("ecall" : "+r"(reg0) : "r"(reg1), "r"(reg2), "r"(reg3), "r"(reg4), "r"(reg5), "r"(reg7) : "memory");
+
+ return reg0;
+}
+
+#endif // TA_SYSCALL_H
diff --git a/include/tuxanci2/text.h b/include/tuxanci2/text.h
@@ -0,0 +1,31 @@
+#ifndef TA_TEXT_H
+#define TA_TEXT_H
+
+#include <tuxanci2/syscall.h>
+#include <tuxanci2/abi.h>
+#include <stdint.h>
+#include <stddef.h>
+
+typedef uint32_t ta_text;
+
+static inline ta_text ta_text_create(const char *text, uint32_t size, uint32_t color, uint32_t border_width, uint32_t border_color) {
+ return ta_syscall(TA_SYS_TEXT_CREATE, (uint32_t)(size_t)text, size, color, border_width, border_color, 0);
+}
+
+static inline uint32_t ta_text_width(ta_text text) {
+ return ta_syscall(TA_SYS_TEXT_WIDTH, text, 0, 0, 0, 0, 0);
+}
+
+static inline uint32_t ta_text_height(ta_text text) {
+ return ta_syscall(TA_SYS_TEXT_HEIGHT, text, 0, 0, 0, 0, 0);
+}
+
+static inline void ta_text_draw(ta_text text, uint32_t x, uint32_t y) {
+ ta_syscall(TA_SYS_TEXT_DRAW, text, x, y, 0, 0, 0);
+}
+
+static inline void ta_text_destroy(ta_text text) {
+ ta_syscall(TA_SYS_TEXT_DESTROY, text, 0, 0, 0, 0, 0);
+}
+
+#endif // TA_TEXT_H
diff --git a/linker.ld b/linker.ld
@@ -0,0 +1,43 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+PROVIDE(__ram_size = 0x10000);
+PROVIDE(__stack_size = 0x2000);
+__heap_end = __ram_size - __stack_size;
+
+SECTIONS {
+ . = 0;
+
+ .magic_number : {
+ BYTE(0x54) /* T */
+ BYTE(0x41) /* A */
+ BYTE(0x42) /* B */
+ BYTE(0x43) /* C */
+ }
+
+ .text : {
+ KEEP(*(.text.start))
+ *(.text .text.*)
+ }
+
+ .rodata : {
+ *(.rodata .rodata.* .srodata .srodata.*)
+ }
+
+ .data : {
+ *(.data .data.* .sdata .sdata.*)
+ }
+
+ .bss : {
+ *(.bss .bss.* .sbss .sbss.* COMMON)
+ }
+
+ . = ALIGN(8);
+ __heap_start = .;
+
+ /DISCARD/ : {
+ *
+ }
+
+ ASSERT(__heap_start + 64 <= __heap_end, "no room left for heap")
+}
diff --git a/sdk.mk b/sdk.mk
@@ -0,0 +1,62 @@
+CROSS ?= riscv32-unknown-elf-
+
+CC := $(CROSS)gcc
+OBJCOPY := $(CROSS)objcopy
+
+BUILD := build
+SDK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
+
+OPT := 2
+MARCH := rv32im
+MABI := ilp32
+
+LINKER_SCRIPT := linker.ld
+
+CFLAGS := -O$(OPT) -march=$(MARCH) -mabi=$(MABI) -ffreestanding -nostdinc -fno-builtin -fno-pic -fno-stack-protector -fno-tree-loop-distribute-patterns -Wall -Wextra -I$(SDK_DIR)include -I$(SDK_DIR)../include
+ASFLAGS := -march=$(MARCH) -mabi=$(MABI) -I$(SDK_DIR)include -I$(SDK_DIR)../include
+LDFLAGS := -march=$(MARCH) -mabi=$(MABI) -nostdlib -static -T $(SDK_DIR)$(LINKER_SCRIPT) -Wl,--gc-sections
+
+SDK_SRCS := $(shell find $(SDK_DIR)src -type f \( -name '*.c' -o -name '*.S' \))
+GAME_SRCS := $(shell find src -type f \( -name '*.c' -o -name '*.S' \))
+
+SDK_OBJS := $(patsubst $(SDK_DIR)%, $(BUILD)/sdk/%, $(SDK_SRCS))
+GAME_OBJS := $(patsubst %, $(BUILD)/game/%, $(GAME_SRCS))
+
+OBJS := $(SDK_OBJS:.c=.o)
+OBJS := $(OBJS:.S=.o)
+GAME_OBJS := $(GAME_OBJS:.c=.o)
+GAME_OBJS := $(GAME_OBJS:.S=.o)
+OBJS += $(GAME_OBJS)
+
+DEPS := $(OBJS:.o=.d)
+
+all: $(BUILD)/$(TARGET).tab
+
+$(BUILD)/$(TARGET).tab: $(BUILD)/$(TARGET).elf
+ $(OBJCOPY) -O binary $< $@
+
+$(BUILD)/$(TARGET).elf: $(OBJS)
+ $(CC) $(LDFLAGS) $^ -o $@
+
+$(BUILD)/sdk/%.o: $(SDK_DIR)%.c
+ @mkdir -p $(@D)
+ $(CC) $(CFLAGS) -MMD -MP -c $< -o $@
+
+$(BUILD)/sdk/%.o: $(SDK_DIR)%.S
+ @mkdir -p $(@D)
+ $(CC) $(ASFLAGS) -MMD -MP -c $< -o $@
+
+$(BUILD)/game/%.o: %.c
+ @mkdir -p $(@D)
+ $(CC) $(CFLAGS) -MMD -MP -c $< -o $@
+
+$(BUILD)/game/%.o: %.S
+ @mkdir -p $(@D)
+ $(CC) $(ASFLAGS) -MMD -MP -c $< -o $@
+
+clean:
+ rm -rf $(BUILD)
+
+-include $(DEPS)
+
+.PHONY: all clean
diff --git a/src/crt0.c b/src/crt0.c
@@ -0,0 +1,21 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <tuxanci2/abi.h>
+#include <tuxanci2/syscall.h>
+
+extern int main(void);
+
+static inline void ta_ready(void (*stub)(uint32_t)) {
+ ta_syscall(TA_SYS_READY, (uint32_t)(size_t)stub, 0, 0, 0, 0, 0);
+}
+
+static inline void ta_return(uint32_t value) {
+ ta_syscall(TA_SYS_RETURN, value, 0, 0, 0, 0, 0);
+
+ while (1) {}
+}
+
+__attribute__((section(".text.start"))) void _start(void) {
+ main();
+ ta_ready(ta_return);
+}
diff --git a/src/stdlib.c b/src/stdlib.c
@@ -0,0 +1,259 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#define HEAP_ALIGNMENT 8u
+#define HEADER_SIZE 8u
+#define MIN_BLOCK 16u
+#define MAX_REQUEST 0x7FFFFFF0u
+#define USED_MAGIC 0xA110C8EDu
+
+typedef struct free_block free_block;
+
+struct free_block {
+ uint32_t size;
+ uint32_t used;
+ free_block *next;
+};
+
+extern uint8_t __heap_start[];
+extern uint8_t __heap_end[];
+
+static free_block *free_list;
+static uint8_t *heap_low;
+static uint8_t *heap_high;
+static uint32_t heap_ready;
+
+static size_t align_up(size_t value) {
+ return (value + (HEAP_ALIGNMENT - 1)) & ~(size_t)(HEAP_ALIGNMENT - 1);
+}
+
+static uint32_t request_to_block(size_t size) {
+ uint32_t block = (uint32_t)align_up(size) + HEADER_SIZE;
+
+ return block < MIN_BLOCK ? MIN_BLOCK : block;
+}
+
+static void heap_init(void) {
+ size_t start = align_up((size_t)__heap_start);
+ size_t end = (size_t)__heap_end & ~(size_t)(HEAP_ALIGNMENT - 1);
+
+ heap_ready = 1;
+ free_list = NULL;
+ heap_low = (uint8_t *)start;
+ heap_high = (uint8_t *)end;
+
+ if (end < start + MIN_BLOCK) {
+ return;
+ }
+
+ free_block *block = (free_block *)start;
+
+ block->size = (uint32_t)(end - start);
+ block->used = 0;
+ block->next = NULL;
+ free_list = block;
+}
+
+static void release(free_block *block) {
+ free_block *previous = NULL;
+ free_block *current = free_list;
+
+ block->used = 0;
+
+ while (current != NULL && current < block) {
+ previous = current;
+ current = current->next;
+ }
+
+ block->next = current;
+
+ if (previous != NULL) {
+ previous->next = block;
+ } else {
+ free_list = block;
+ }
+
+ if (current != NULL && (uint8_t *)block + block->size == (uint8_t *)current) {
+ block->size += current->size;
+ block->next = current->next;
+ }
+
+ if (previous != NULL && (uint8_t *)previous + previous->size == (uint8_t *)block) {
+ previous->size += block->size;
+ previous->next = block->next;
+ }
+}
+
+static void unlink_block(free_block *target) {
+ free_block **link = &free_list;
+
+ while (*link != NULL) {
+ if (*link == target) {
+ *link = target->next;
+
+ return;
+ }
+
+ link = &(*link)->next;
+ }
+}
+
+static void trim(free_block *block, uint32_t need) {
+ if (block->size - need < MIN_BLOCK) {
+ return;
+ }
+
+ free_block *tail = (free_block *)((uint8_t *)block + need);
+
+ tail->size = block->size - need;
+ block->size = need;
+
+ release(tail);
+}
+
+static free_block *block_from_pointer(void *pointer) {
+ if (!heap_ready) {
+ return NULL;
+ }
+
+ uint8_t *address = (uint8_t *)pointer;
+
+ if (address < heap_low + HEADER_SIZE || address >= heap_high) {
+ return NULL;
+ }
+
+ if (((size_t)address & (HEAP_ALIGNMENT - 1)) != 0) {
+ return NULL;
+ }
+
+ free_block *block = (free_block *)(address - HEADER_SIZE);
+
+ if (block->used != USED_MAGIC) {
+ return NULL;
+ }
+
+ return block;
+}
+
+void *malloc(size_t size) {
+ if (!heap_ready) {
+ heap_init();
+ }
+
+ if (size == 0 || size > MAX_REQUEST) {
+ return NULL;
+ }
+
+ uint32_t need = request_to_block(size);
+ free_block *best = NULL;
+
+ for (free_block *current = free_list; current != NULL; current = current->next) {
+ if (current->size < need) {
+ continue;
+ }
+
+ if (best == NULL || current->size < best->size) {
+ best = current;
+
+ if (current->size == need) {
+ break;
+ }
+ }
+ }
+
+ if (best == NULL) {
+ return NULL;
+ }
+
+ unlink_block(best);
+ trim(best, need);
+ best->used = USED_MAGIC;
+
+ return (uint8_t *)best + HEADER_SIZE;
+}
+
+void free(void *pointer) {
+ if (pointer == NULL) {
+ return;
+ }
+
+ free_block *block = block_from_pointer(pointer);
+
+ if (block == NULL) {
+ return;
+ }
+
+ release(block);
+}
+
+void *calloc(size_t count, size_t size) {
+ if (size != 0 && count > MAX_REQUEST / size) {
+ return NULL;
+ }
+
+ size_t total = count * size;
+ void *pointer = malloc(total);
+
+ if (pointer != NULL) {
+ memset(pointer, 0, total);
+ }
+
+ return pointer;
+}
+
+void *realloc(void *pointer, size_t size) {
+ if (pointer == NULL) {
+ return malloc(size);
+ }
+
+ free_block *block = block_from_pointer(pointer);
+
+ if (block == NULL) {
+ return NULL;
+ }
+
+ if (size == 0) {
+ free(pointer);
+
+ return NULL;
+ }
+
+ if (size > MAX_REQUEST) {
+ return NULL;
+ }
+
+ uint32_t need = request_to_block(size);
+
+ if (need <= block->size) {
+ trim(block, need);
+
+ return pointer;
+ }
+
+ free_block *neighbor = free_list;
+
+ while (neighbor != NULL && (uint8_t *)neighbor < (uint8_t *)block + block->size) {
+ neighbor = neighbor->next;
+ }
+
+ if (neighbor != NULL && (uint8_t *)neighbor == (uint8_t *)block + block->size && block->size + neighbor->size >= need) {
+ unlink_block(neighbor);
+ block->size += neighbor->size;
+ trim(block, need);
+
+ return pointer;
+ }
+
+ void *grown = malloc(size);
+
+ if (grown == NULL) {
+ return NULL;
+ }
+
+ memcpy(grown, pointer, block->size - HEADER_SIZE);
+ free(pointer);
+
+ return grown;
+}
diff --git a/src/string.c b/src/string.c
@@ -0,0 +1,53 @@
+#include <stdint.h>
+#include <stddef.h>
+
+void *memcpy(void *destination, const void *source, size_t count) {
+ uint8_t *destination_bytes = (uint8_t *)destination;
+ const uint8_t *source_bytes = (const uint8_t *)source;
+
+ for (size_t i = 0; i < count; i++) {
+ destination_bytes[i] = source_bytes[i];
+ }
+
+ return destination;
+}
+
+void *memmove(void *destination, const void *source, size_t count) {
+ uint8_t *destination_bytes = (uint8_t *)destination;
+ const uint8_t *source_bytes = (const uint8_t *)source;
+
+ if (destination_bytes < source_bytes) {
+ for (size_t i = 0; i < count; i++) {
+ destination_bytes[i] = source_bytes[i];
+ }
+ } else {
+ for (size_t i = count; i > 0; i--) {
+ destination_bytes[i - 1] = source_bytes[i - 1];
+ }
+ }
+
+ return destination;
+}
+
+void *memset(void *destination, int value, size_t count) {
+ uint8_t *destination_bytes = (uint8_t *)destination;
+
+ for (size_t i = 0; i < count; i++) {
+ destination_bytes[i] = (uint8_t)value;
+ }
+
+ return destination;
+}
+
+int memcmp(const void *first, const void *second, size_t count) {
+ const uint8_t *first_bytes = (const uint8_t *)first;
+ const uint8_t *second_bytes = (const uint8_t *)second;
+
+ for (size_t i = 0; i < count; i++) {
+ if (first_bytes[i] != second_bytes[i]) {
+ return first_bytes[i] < second_bytes[i] ? -1 : 1;
+ }
+ }
+
+ return 0;
+}