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:
A.gitignore | 1+
ALICENSE | 18++++++++++++++++++
AREADME.md | 2++
Ainclude/stdbool.h | 10++++++++++
Ainclude/stddef.h | 8++++++++
Ainclude/stdint.h | 12++++++++++++
Ainclude/stdlib.h | 20++++++++++++++++++++
Ainclude/string.h | 11+++++++++++
Ainclude/tuxanci2/abi.h | 25+++++++++++++++++++++++++
Ainclude/tuxanci2/display.h | 23+++++++++++++++++++++++
Ainclude/tuxanci2/fixed.h | 10++++++++++
Ainclude/tuxanci2/image.h | 23+++++++++++++++++++++++
Ainclude/tuxanci2/keyboard.h | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/tuxanci2/screen.h | 22++++++++++++++++++++++
Ainclude/tuxanci2/syscall.h | 20++++++++++++++++++++
Ainclude/tuxanci2/text.h | 31+++++++++++++++++++++++++++++++
Alinker.ld | 43+++++++++++++++++++++++++++++++++++++++++++
Asdk.mk | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/crt0.c | 21+++++++++++++++++++++
Asrc/stdlib.c | 259+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/string.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +}