aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/libc
diff options
context:
space:
mode:
authorJohn Wilbert M. Villamor <lameguy64@gmail.com>2019-04-06 10:11:07 +0800
committerJohn Wilbert M. Villamor <lameguy64@gmail.com>2019-04-06 10:11:07 +0800
commitf3e040230772f978540a71aea43dfde200992922 (patch)
treebd8ca31b72dd01e24980b073854e263589530f56 /libpsn00b/libc
downloadpsn00bsdk-f3e040230772f978540a71aea43dfde200992922.tar.gz
First commit
Diffstat (limited to 'libpsn00b/libc')
-rw-r--r--libpsn00b/libc/makefile38
-rw-r--r--libpsn00b/libc/malloc.s216
-rw-r--r--libpsn00b/libc/memcmp.s31
-rw-r--r--libpsn00b/libc/memcpy.s28
-rw-r--r--libpsn00b/libc/memmove.s29
-rw-r--r--libpsn00b/libc/memset.s25
-rw-r--r--libpsn00b/libc/printf.c788
-rw-r--r--libpsn00b/libc/rand.s38
-rw-r--r--libpsn00b/libc/readme.txt48
-rw-r--r--libpsn00b/libc/scanf.c427
-rw-r--r--libpsn00b/libc/start.s39
-rw-r--r--libpsn00b/libc/string.c301
12 files changed, 2008 insertions, 0 deletions
diff --git a/libpsn00b/libc/makefile b/libpsn00b/libc/makefile
new file mode 100644
index 0000000..feadbdb
--- /dev/null
+++ b/libpsn00b/libc/makefile
@@ -0,0 +1,38 @@
+# Run using make (Linux) or gmake (BSD)
+# Part of the PSn00bSDK Project
+# 2019 Lameguy64 / Meido-Tek Productions
+
+PREFIX = mipsel-unknown-elf-
+
+TARGET = ../libc.a
+
+CFILES = $(notdir $(wildcard ./*.c))
+AFILES = $(notdir $(wildcard ./*.s))
+OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o))
+
+INCLUDE = -I../include
+
+CFLAGS = -O2 -msoft-float -fno-builtin -fdata-sections -ffunction-sections -Wa,--strip-local-absolute
+AFLAGS = -msoft-float -Wa,--strip-local-absolute
+
+CC = $(PREFIX)gcc
+AS = $(PREFIX)as
+AR = $(PREFIX)ar
+RANLIB = $(PREFIX)ranlib
+
+all: $(TARGET)
+
+$(TARGET): $(OFILES)
+ $(AR) cr $(TARGET) $(OFILES)
+ $(RANLIB) $(TARGET)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+clean:
+ rm -Rf build $(TARGET)
diff --git a/libpsn00b/libc/malloc.s b/libpsn00b/libc/malloc.s
new file mode 100644
index 0000000..20e5371
--- /dev/null
+++ b/libpsn00b/libc/malloc.s
@@ -0,0 +1,216 @@
+# Custom first-fit malloc routines by Lameguy64
+# Part of the PSn00bSDK Project
+
+.set noreorder
+
+.set ND_PREV, 0 # Address to previous block (NULL if starting block)
+.set ND_NEXT, 4 # Address to next block (NULL if end block)
+.set ND_SIZE, 8 # Size of block
+.set ND_HSIZ, 12
+
+.section .text
+
+
+# Stupid small function just to get bss end
+# due to GCC insisting externs to be gp relative
+.global GetBSSend
+.type GetBSSend, @function
+GetBSSend:
+ la $v0, _end
+ jr $ra
+ nop
+
+
+# Initializes the heap for malloc
+# a0 - Starting address of heap
+# a1 - Size of memory heap
+#
+.global InitHeap
+.type InitHeap, @function
+InitHeap:
+ la $v0, _malloc_addr
+ sw $a0, 0($v0)
+ la $v0, _malloc_size
+ sw $a1, 0($v0)
+
+ sw $0 , ND_PREV($a0) # Set heap header
+ sw $0 , ND_NEXT($a0)
+ jr $ra
+ sw $0 , ND_SIZE($a0)
+
+
+# Allocates a block of memory in the heap
+# a0 - Size of memory block to allocate.
+#
+.global malloc
+.type malloc, @function
+malloc:
+ addiu $a0, 3 # Round size to a multiple of 4
+ srl $a0, 2
+
+ la $a2, _malloc_addr
+ lw $a2, 0($a2)
+ sll $a0, 2
+
+.find_next:
+
+ move $a1, $a2
+
+ lw $a2, ND_NEXT($a1) # Get block header
+ lw $v1, ND_SIZE($a1)
+
+ subu $v0, $a2, $a1 # Compute space between current and next
+
+ beqz $v1, .empty_block # Occupy empty block (if size = 0)
+ nop
+
+ beqz $a2, .new_block # Allocate a new block (if no next)
+ nop
+
+ addiu $v0, -(ND_HSIZ*2) # Compute remaining space of block
+ subu $v0, $v1
+
+ blt $v0, $a0, .find_next # Search for the next block if space is not big enough
+ nop
+
+ # Perform a block split using remaining space of current block
+
+ addiu $v0, $a1, ND_HSIZ # Compute address for new header
+ addu $v0, $v1
+
+ sw $a1, ND_PREV($v0) # Set the new block header
+ sw $a2, ND_NEXT($v0)
+ sw $a0, ND_SIZE($v0)
+
+ sw $v0, ND_NEXT($a1) # Update previous and next blocks
+ sw $v0, ND_PREV($a2)
+
+ jr $ra
+ addiu $v0, ND_HSIZ
+
+.empty_block: # Occupy an empty block
+
+ beqz $a2, .no_next # Skip size calculation if there's no next
+ nop
+
+ addiu $v0, -ND_HSIZ
+ blt $v0, $a0, .find_next
+ nop
+
+ b .skip_space_check
+ nop
+
+.no_next:
+
+ la $v1, _malloc_addr # Check if there's enough space for a block
+ lw $v1, 0($v1)
+ la $v0, _malloc_size
+ lw $v0, 0($v0)
+
+ subu $v1, $a1, $v1
+ addu $v1, $a0
+ addiu $v1, ND_HSIZ
+
+ bgt $v1, $v0, .no_space
+ nop
+
+.skip_space_check:
+
+ sw $a0, ND_SIZE($a1)
+ jr $ra # Return address
+ addiu $v0, $a1, ND_HSIZ
+
+.new_block: # Create a new block
+
+ addu $a2, $a1, $v1 # Compute address for new block
+ addiu $a2, ND_HSIZ
+
+ la $v1, _malloc_addr
+ lw $v1, 0($v1)
+ la $v0, _malloc_size
+ lw $v0, 0($v0)
+
+ subu $v1, $a2, $v1
+ addu $v1, $a0
+ addiu $v1, ND_HSIZ
+
+ bgt $v1, $v0, .no_space # Reject if it exceeds specified size
+ nop
+
+ sw $a1, ND_PREV($a2)
+ sw $0 , ND_NEXT($a2)
+ sw $a0, ND_SIZE($a2)
+
+ sw $a2, ND_NEXT($a1)
+
+ jr $ra # Return address
+ addiu $v0, $a2, ND_HSIZ
+
+.no_space: # Return a null if no space can be found
+ jr $ra
+ move $v0, $0
+
+
+# Allocates a block of memory in block units and zero fills the
+# allocated block.
+# a0 - Block size.
+# a1 - Number of blocks to allocate
+#
+.global calloc
+.type calloc, @function
+calloc:
+ mult $a0, $a1
+ addiu $sp, -4
+ sw $ra, 0($sp)
+
+ jal malloc
+ mflo $a0
+
+ move $a0, $v0
+ mflo $a1
+.clear_loop:
+ sw $0 , 0($a0)
+ addi $a1, 4
+ bgtz $a1, .clear_loop
+ addiu $a0, 4
+
+ lw $ra, 0($sp)
+ addiu $sp, 4
+ jr $ra
+ nop
+
+
+# Deallocates an allocated block
+# a0 - An address returned by malloc to deallocate
+#
+.global free
+.type free, @function
+free:
+
+ addiu $a0, -ND_HSIZ
+ lw $a1, ND_PREV($a0)
+ lw $a2, ND_NEXT($a0)
+
+ beqz $a1, .is_start # Check if block is a starting block
+ nop
+
+ beqz $a2, .is_end
+ nop
+
+ # Unlink
+
+ sw $a2, ND_NEXT($a1)
+ jr $ra
+ sw $a1, ND_PREV($a2)
+
+.is_end: # Unlinks the ending block
+ jr $ra
+ sw $0 , ND_NEXT($a1)
+.is_start: # Simply set size to 0 if starting block
+ jr $ra
+ sw $0 , ND_SIZE($a0)
+
+
+# Internal variables
+.comm _malloc_addr, 4, 4
+.comm _malloc_size, 4, 4
diff --git a/libpsn00b/libc/memcmp.s b/libpsn00b/libc/memcmp.s
new file mode 100644
index 0000000..b8b495d
--- /dev/null
+++ b/libpsn00b/libc/memcmp.s
@@ -0,0 +1,31 @@
+# High speed ASM memcmp implementation by Lameguy64
+#
+# Part of PSn00bSDK
+
+.set noreorder
+
+.section .text
+
+# Arguments:
+# a0 - buffer 1 address
+# a1 - buffer 2 address
+# a2 - bytes to compare
+.global memcmp
+.type memcmp, @function
+memcmp:
+ blez $a2, .exit
+ addi $a2, -1
+ lbu $v0, 0($a0)
+ lbu $v1, 0($a1)
+ addiu $a0, 1
+ bne $v0, $v1, .mismatch
+ addiu $a1, 1
+ b memcmp
+ nop
+.mismatch:
+ jr $ra
+ sub $v0, $v1
+.exit:
+ jr $ra
+ move $v0, $0
+ \ No newline at end of file
diff --git a/libpsn00b/libc/memcpy.s b/libpsn00b/libc/memcpy.s
new file mode 100644
index 0000000..e1a4e30
--- /dev/null
+++ b/libpsn00b/libc/memcpy.s
@@ -0,0 +1,28 @@
+# High speed ASM memcpy implementation by Lameguy64
+#
+# Part of PSn00bSDK
+
+.set noreorder
+
+.section .text
+
+# Arguments:
+# a0 - destination address
+# a1 - source adress
+# a2 - bytes to copy
+.global memcpy
+.type memcpy, @function
+memcpy:
+ move $v0, $a0
+.loop:
+ blez $a2, .exit
+ addi $a2, -1
+ lbu $a3, 0($a1)
+ addiu $a1, 1
+ sb $a3, 0($a0)
+ b .loop
+ addiu $a0, 1
+.exit:
+ jr $ra
+ nop
+ \ No newline at end of file
diff --git a/libpsn00b/libc/memmove.s b/libpsn00b/libc/memmove.s
new file mode 100644
index 0000000..961e71f
--- /dev/null
+++ b/libpsn00b/libc/memmove.s
@@ -0,0 +1,29 @@
+.set noreorder
+
+.section .text
+
+# Arguments
+# a0 - destination address
+# a1 - source address
+# a2 - bytes to move
+.global memmove
+.type memmove, @function
+memmove:
+ move $v0, $a0
+ addu $a0, $a2
+ addu $a1, $a2
+ addiu $a0, -1
+ addiu $a1, -1
+.loop:
+ blez $a2, .exit
+ addi $a2, -1
+ lbu $v1, 0($a1)
+ addiu $a1, -1
+ sb $v1, 0($a0)
+ addiu $a0, -1
+ b .loop
+ nop
+.exit:
+ jr $ra
+ nop
+ \ No newline at end of file
diff --git a/libpsn00b/libc/memset.s b/libpsn00b/libc/memset.s
new file mode 100644
index 0000000..f7d86b1
--- /dev/null
+++ b/libpsn00b/libc/memset.s
@@ -0,0 +1,25 @@
+# High speed ASM memset implementation by Lameguy64
+#
+# Part of PSn00bSDK
+
+.set noreorder
+
+.section .text
+
+# Arguments:
+# a0 - address to buffer
+# a1 - value to set
+# a2 - bytes to set
+.global memset
+.type memset,@function
+memset:
+ move $v0, $a0
+ blez $a2, .exit
+ addi $a2, -1
+ sb $a1, 0($a0)
+ b memset
+ addiu $a0, 1
+.exit:
+ jr $ra
+ nop
+ \ No newline at end of file
diff --git a/libpsn00b/libc/printf.c b/libpsn00b/libc/printf.c
new file mode 100644
index 0000000..7350648
--- /dev/null
+++ b/libpsn00b/libc/printf.c
@@ -0,0 +1,788 @@
+/* printf.c
+ *
+ * Inherited from the PSXSDK C library
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define SPRINTF_ALT_FLAG (1<<0)
+#define SPRINTF_ZERO_FLAG (1<<1)
+#define SPRINTF_NEGFIELD_FLAG (1<<2)
+#define SPRINTF_SPACE_FLAG (1<<3)
+#define SPRINTF_SIGN_FLAG (1<<4)
+
+// sprintf() macros to calculate the real padding and to write it
+// these were made to not repeat the code in the function
+// they can only be used in sprintf()
+
+// sprintf macros START
+
+#define calculate_real_padding() \
+ y = 1; \
+ \
+ for(x=0;x<=19;x++) \
+ { \
+ if(x == 0) \
+ pad_quantity--; \
+ else \
+ { \
+ if(arg / y) \
+ pad_quantity--; \
+ } \
+ \
+ y *= 10; \
+ } \
+ \
+ if(pad_quantity < 0) pad_quantity = 0;
+
+/*#define calculate_real_padding_hex() \
+ for (x = 0; x < 8; x++) \
+ { \
+ if(x == 0) \
+ pad_quantity--; \
+ else \
+ { \
+ if((arg >> (x * 4)) & 0xf) \
+ pad_quantity--; \
+ } \
+ }*/
+
+#define calculate_real_padding_hex() \
+ last = 0; \
+ for (x = 0; x < 16; x++) \
+ if((arg >> (x * 4)) & 0xf) \
+ last = x; \
+ \
+ pad_quantity = (pad_quantity - 1) - last; \
+ if(pad_quantity < 0) pad_quantity = 0;
+
+#define write_padding() \
+ if(!(flags & SPRINTF_NEGFIELD_FLAG)) \
+ for(x = 0; x < pad_quantity; x++) \
+ { \
+ if(flags & SPRINTF_ZERO_FLAG) \
+ put_in_string(string, ssz, '0', string_pos++); \
+ else \
+ put_in_string(string, ssz, ' ', string_pos++); \
+ }
+
+#define write_neg_padding() \
+ if(flags & SPRINTF_NEGFIELD_FLAG) \
+ { \
+ for(x = 0; x < pad_quantity; x++) \
+ put_in_string(string, ssz, ' ', string_pos++);\
+ }
+
+// sprintf macros END
+
+enum
+{
+ SPRINTF_SIZE_CHAR,
+ SPRINTF_SIZE_SHORT,
+ SPRINTF_SIZE_INT,
+ SPRINTF_SIZE_LONG,
+ SPRINTF_SIZE_LONG_LONG,
+};
+
+unsigned int get_arg_in_size(int size, unsigned long long *arg, unsigned int check_sign)
+{
+ int s = 0;
+
+ switch(size)
+ {
+ case SPRINTF_SIZE_CHAR:
+ *arg &= 0xff;
+
+ if(check_sign)
+ {
+ if(*arg & (1<<7))
+ {
+ *arg |= 0xffffff00;
+ *arg = ~(*arg - 1);
+ s = 1;
+ }
+ }
+ break;
+ case SPRINTF_SIZE_SHORT:
+ *arg &= 0xffff;
+
+ if(check_sign)
+ {
+ if(*arg & (1<<15))
+ {
+ *arg |= 0xffff0000;
+ *arg = ~(*arg - 1);
+ s = 1;
+ }
+ }
+ break;
+
+// sizeof(long) == sizeof(int) on 32bit, so this will suffice for the psx
+
+ case SPRINTF_SIZE_INT:
+ case SPRINTF_SIZE_LONG:
+ *arg &= 0xffffffff;
+
+ if(check_sign)
+ {
+ if(*arg & (1<<31))
+ {
+ *arg |= (long long)0xffffffff00000000;
+ *arg = ~(*arg - 1);
+ s = 1;
+ }
+ }
+ break;
+
+ case SPRINTF_SIZE_LONG_LONG:
+ if(check_sign)
+ {
+ if(*arg & ((long long)1<<63))
+ {
+ *arg = ~(*arg - 1);
+ s = 1;
+ }
+ }
+ break;
+ }
+
+ return s;
+}
+
+int put_in_string(char *string, unsigned int sz, char c, int pos)
+{
+ if(pos>=sz)
+ return 0;
+ else
+ string[pos] = c;
+
+ return 1;
+}
+
+int libc_ulltoa(unsigned long long i, char *dst, int n)
+{
+ int x, y;
+ unsigned long long a, b;
+ int empty_digit = 1;
+ int sp=0;
+ int n2=0;
+
+ for(x=20;x>=0;x--)
+ {
+ a = 1;
+ for(y = 0; y<x; y++)
+ a *= 10;
+
+ b = (i/a);
+
+ if(b>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ {
+ i -= b*a;
+
+ //put_in_string(string, ssz, b + '0', string_pos++);
+ if(n2!=n)
+ {
+ //printf("n2=%d\n",n2);
+ dst[sp++] = b + '0';
+ n2++;
+ }
+ }
+ }
+
+ if(n2!=n)dst[sp] = 0;
+
+ return n2;
+}
+
+void libc_float_to_string(float fl, char *dst, int n)
+{
+ unsigned int *p = (unsigned int*)&fl;
+ unsigned long long i = 0;
+ unsigned long long f = 0;
+ int e, m, s;
+ int x, y;
+ unsigned long long z;
+
+ s = *p >> 31;
+
+ e = (*p >> 23) & 0xff;
+
+ m = *p & 0x7fffff;
+
+ if(e == 255 && m == 0) // Infinity
+ {
+ if(s) strncpy(dst, "-inf", n);
+ else strncpy(dst, "inf", n);
+ }else if(e == 255 && m != 0) // NaN
+ {
+ strncpy(dst, "nan", n);
+ }
+ else
+ {
+ e -= 127;
+ m |= 1<<23;
+
+
+
+ for(x = 23; x >= 0; x--)
+ {
+ if(m & (1<<x))
+ {
+ if(e >= 0)
+ {
+ z = 1;
+ for(y=0;y<e;y++)
+ z*=2;
+
+ i+=z;
+ }
+ else
+ {
+ z = 5000000000000000000;
+ for(y = 1; y < -e; y++)
+ z /= 2;
+
+ f+=z;
+ }
+ }
+ e--;
+ }
+
+ if(s && n)
+ {
+ *(dst++) = '-';
+ n--;
+ }
+
+ x = libc_ulltoa(i, dst, n);
+ n-=x;
+ dst+=x;
+
+ if(n)
+ {
+ *(dst++) = '.';
+ n--;
+ if(n)
+ {
+ x = libc_ulltoa(f, dst, n<6?n:6);
+ n-=x;
+ dst+=x;
+
+ if(n)
+ *dst=0;
+ }
+ }
+ }
+}
+
+void libc_double_to_string(double fl, char *dst, int n)
+{
+ unsigned long long *p = (unsigned long long *)&fl;
+ unsigned long long i = 0;
+ unsigned long long f = 0;
+ unsigned long long m, s;
+ long long e;
+ int x, y;
+ unsigned long long z;
+
+ s = *p >> 63;
+
+ e = (*p >> 52) & 0x7ff;
+ //printf("%d\n", e);
+
+ m = *p & 0xfffffffffffff;
+
+ for(x=0;x<52;x++)
+ if(m&((unsigned long long)1<<(52-x))) putchar('1'); else putchar('0');
+
+ if(e == 255 && m == 0) // Infinity
+ {
+ if(s) strncpy(dst, "-inf", n);
+ else strncpy(dst, "inf", n);
+ }else if(e == 255 && m != 0) // NaN
+ {
+ strncpy(dst, "nan", n);
+ }
+ else
+ {
+ e -= 1023;
+ m |= (unsigned long long)1<<52;
+
+ for(x = 52; x >= 0; x--)
+ {
+ if(m & ((unsigned long long)1<<x))
+ {
+ if(e >= 0)
+ {
+ z = (long long)1<<e;
+
+ i+=z;
+ }
+ else
+ {
+ z = 5000000000000000000;
+ z >>= -(e + 1);
+
+ f+=z;
+ }
+ }
+ e--;
+ }
+
+ if(s && n)
+ {
+ *(dst++) = '-';
+ n--;
+ }
+
+ x = libc_ulltoa(i, dst, n);
+ n-=x;
+ dst+=x;
+
+ if(n)
+ {
+ *(dst++) = '.';
+ n--;
+ if(n)
+ libc_ulltoa(f, dst, n<6?n:6);
+ }
+ }
+}
+
+char libc_sprintf_floatbuf[64];
+
+int vsnprintf(char *string, unsigned int size, char *fmt, va_list ap)
+{
+ int string_pos,fmt_pos;
+ int l;
+ unsigned long long arg;
+ unsigned char *argcp;
+ unsigned char *argcp_tmp;
+ int directive_coming = 0;
+ int alternate_form = 0;
+ int flags = 0;
+ int argsize = 2; // int
+ int x, y;
+ unsigned long long a, b;
+ int empty_digit;
+ int ssz = size - 1;
+ int zero_flag_imp = 0;
+ int pad_quantity = 0;
+ int last;
+
+ l = strlen(fmt);
+
+ string_pos = 0;
+
+ for(fmt_pos=0;fmt_pos<l;fmt_pos++)
+ {
+ if(directive_coming)
+ {
+ switch(fmt[fmt_pos])
+ {
+ case '%':
+ put_in_string(string, ssz, '%', string_pos++);
+ directive_coming = 0;
+ break;
+ case ' ':
+ flags |= SPRINTF_SPACE_FLAG;
+ break;
+ case '#': // Specify alternate form
+ flags |= SPRINTF_ALT_FLAG;
+ break;
+ case '+': // Specify sign in signed conversions
+ flags |= SPRINTF_SIGN_FLAG;
+ break;
+ case '0': // Padding with zeros...
+ if(zero_flag_imp == 0)
+ {
+ flags |= SPRINTF_ZERO_FLAG;
+ zero_flag_imp = 1;
+ //printf("Zero padding enabled!\n");
+ }
+ else
+ {
+ pad_quantity *= 10;
+ //printf("pad_quantity = %d\n", pad_quantity);
+ }
+ break;
+ case '1' ... '9': // '...' cases are a GNU extension,
+ // but they simplify a lot
+
+ pad_quantity *= 10;
+ pad_quantity += fmt[fmt_pos] - '0';
+ zero_flag_imp = 1;
+
+ //printf("pad_quantity = %d\n", pad_quantity);
+ break;
+ case '-': // Negative field flag
+ if(flags & SPRINTF_ZERO_FLAG)
+ flags &= ~SPRINTF_ZERO_FLAG;
+
+ flags |= SPRINTF_NEGFIELD_FLAG;
+ break;
+ case 'h': // Half argument size
+ if(argsize) argsize--;
+ break;
+ case 'l': // Double argument size
+ if(argsize < 2) argsize = 2;
+ else if(argsize < SPRINTF_SIZE_LONG_LONG) argsize++;
+ break;
+ case 'd': // signed decimal
+ case 'i':
+ empty_digit = 1;
+
+ //printf("argsize = %d\n", argsize);
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long);
+
+ if(get_arg_in_size(argsize, &arg, 1))
+ {
+ put_in_string(string, ssz, '-', string_pos++);
+ pad_quantity--;
+ }
+ else
+ {
+ if(flags & SPRINTF_SIGN_FLAG)
+ {
+ put_in_string(string, ssz, '+', string_pos++);
+ pad_quantity--;
+ }
+ }
+
+ /* Calculate how much padding we have to write */
+
+ /*y = 1;
+
+ for(x=0;x<=9;x++)
+ {
+ if(x == 0)
+ pad_quantity--;
+ else
+ {
+ if(arg / y)
+ pad_quantity--;
+ }
+
+ y *= 10;
+ }
+ if(pad_quantity < 0) pad_quantity = 0;*/
+
+ calculate_real_padding();
+
+ //printf("Actual pad quantity = %d\n", pad_quantity);
+
+
+
+ /*if(!(flags & SPRINTF_NEGFIELD_FLAG))
+ {
+ for(x = 0; x < pad_quantity; x++)
+ {
+ if(flags & SPRINTF_ZERO_FLAG)
+ put_in_string(string, ssz, '0', string_pos++);
+ else
+ put_in_string(string, ssz, ' ', string_pos++);
+ }
+ }*/
+
+ write_padding();
+
+ for(x=19;x>=0;x--)
+ {
+ a = 1;
+ for(y = 0; y<x; y++)
+ a *= 10;
+
+ b = (arg/a);
+
+ if(b>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ {
+ arg -= b*a;
+
+ put_in_string(string, ssz, b + '0', string_pos++);
+ }
+ }
+
+ /*if(flags & SPRINTF_NEGFIELD_FLAG)
+ {
+ for(x = 0; x < pad_quantity; x++)
+ put_in_string(string, ssz, ' ', string_pos++);
+ }*/
+ write_neg_padding();
+
+ directive_coming = 0;
+ break;
+ case 'u': // unsigned decimal
+ empty_digit = 1;
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long);
+
+ get_arg_in_size(argsize, &arg, 0);
+
+ calculate_real_padding();
+ write_padding();
+
+ for(x=19;x>=0;x--)
+ {
+ a = 1;
+ for(y = 0; y<x; y++)
+ a *= 10;
+
+
+
+ b = (arg/a);
+
+ if(b>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ {
+ arg -= b*a;
+
+ put_in_string(string, ssz, b + '0', string_pos++);
+ }
+ }
+
+ write_neg_padding();
+
+ directive_coming = 0;
+ break;
+ case 'x': // Hexadecimal
+ case 'X': // Hexadecimal with big letters
+ case 'p': // Hexadecimal with small letters with '0x' prefix
+ empty_digit = 1;
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long int);
+
+ get_arg_in_size(argsize, &arg, 0);
+
+ if(fmt_pos == 'p')
+ flags |= SPRINTF_ALT_FLAG;
+
+ if(flags & SPRINTF_ALT_FLAG)
+ {
+ put_in_string(string, ssz, '0', string_pos++);
+
+ if(fmt[fmt_pos] == 'X')
+ put_in_string(string, ssz, 'X', string_pos++);
+ else
+ put_in_string(string, ssz, 'x', string_pos++);
+ }
+
+ calculate_real_padding_hex();
+ write_padding();
+
+ for(x=15;x>=0;x--)
+ {
+ y = arg >> (x << 2);
+ y &= 0xf;
+
+ if(y>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ {
+ if(y>=0 && y<=9)
+ put_in_string(string, ssz, y + '0', string_pos++);
+ else if(y>=0xA && y<=0xF)
+ {
+ if(fmt[fmt_pos] == 'X')
+ put_in_string(string, ssz, (y - 0xa) + 'A', string_pos++);
+ else
+ put_in_string(string, ssz, (y - 0xa) + 'a', string_pos++);
+ }
+ }
+ }
+
+ write_neg_padding();
+
+ directive_coming = 0;
+ break;
+ case 'c': // character
+ arg = va_arg(ap, int);
+
+ put_in_string(string, ssz, arg & 0xff, string_pos++);
+
+ directive_coming = 0;
+ break;
+ case 's': // string
+ argcp = va_arg(ap, char *);
+ argcp_tmp = argcp;
+
+ if(argcp == NULL)
+ {
+ // Non standard extension, but supported by Linux and the BSDs.
+
+ put_in_string(string, ssz, '(', string_pos++);
+ put_in_string(string, ssz, 'n', string_pos++);
+ put_in_string(string, ssz, 'u', string_pos++);
+ put_in_string(string, ssz, 'l', string_pos++);
+ put_in_string(string, ssz, 'l', string_pos++);
+ put_in_string(string, ssz, ')', string_pos++);
+
+ directive_coming = 0;
+ break;
+ }
+
+ while(*argcp_tmp)
+ {
+ if(pad_quantity > 0) pad_quantity--;
+ argcp_tmp++;
+ }
+
+ if(!(flags & SPRINTF_NEGFIELD_FLAG))
+ {
+ while(pad_quantity > 0)
+ {
+ put_in_string(string,ssz, ' ', string_pos++);
+ pad_quantity--;
+ }
+ }
+
+ while(*argcp)
+ {
+ put_in_string(string, ssz, *argcp, string_pos++);
+ argcp++;
+ }
+
+ if(flags & SPRINTF_NEGFIELD_FLAG)
+ {
+ while(pad_quantity > 0)
+ {
+ put_in_string(string,ssz, ' ', string_pos++);
+ pad_quantity--;
+ }
+ }
+
+ directive_coming = 0;
+ break;
+ case 'o': // Octal
+ empty_digit = 1;
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long);
+
+ for(x=21;x>=0;x--)
+ {
+ y = arg >> (x * 3);
+ y &= 0x7;
+
+ if(y>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ put_in_string(string, ssz, y + '0', string_pos++);
+ }
+
+ directive_coming = 0;
+ break;
+ case '@': // Binary
+ empty_digit = 1;
+
+ if(argsize < SPRINTF_SIZE_LONG_LONG)
+ arg = (unsigned long long)va_arg(ap, unsigned int);
+ else
+ arg = va_arg(ap, unsigned long long);
+
+ for(x=63;x>=0;x--)
+ {
+ y = (arg >> x);
+ y &= 1;
+
+ if(y>=1)
+ empty_digit = 0;
+
+ if(empty_digit == 0 || x == 0)
+ put_in_string(string, ssz, y + '0', string_pos++);
+ }
+
+ directive_coming = 0;
+ break;
+
+ case 'f':
+ libc_double_to_string(va_arg(ap, double), libc_sprintf_floatbuf, 64);
+
+ for(x=0;libc_sprintf_floatbuf[x]!=0;x++)
+ put_in_string(string, ssz, libc_sprintf_floatbuf[x], string_pos++);
+
+ directive_coming = 0;
+ break;
+ case 'n': // Number of characters written
+ *(va_arg(ap,unsigned int*)) = string_pos;
+
+ directive_coming = 0;
+ break;
+ // default
+ }
+ }
+ else
+ {
+ if(fmt[fmt_pos] == '%')
+ {
+ directive_coming = 1;
+ flags = 0;
+ argsize = 2;
+ pad_quantity = 0;
+ zero_flag_imp = 0;
+ }
+ else
+ put_in_string(string, ssz, fmt[fmt_pos], string_pos++);
+ }
+ }
+ string[string_pos] = 0;
+ return string_pos;
+}
+
+int vsprintf(char *string, char *fmt, va_list ap)
+{
+ return vsnprintf(string, 0xffffffff, fmt, ap);
+}
+
+int sprintf(char *string, char *fmt, ...)
+{
+ int r;
+
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ r = vsprintf(string, fmt, ap);
+
+ va_end(ap);
+
+ return r;
+}
+
+int snprintf(char *string, unsigned int size, char *fmt, ...)
+{
+ int r;
+
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ r = vsnprintf(string, size, fmt, ap);
+
+ va_end(ap);
+
+ return r;
+}
diff --git a/libpsn00b/libc/rand.s b/libpsn00b/libc/rand.s
new file mode 100644
index 0000000..a502e87
--- /dev/null
+++ b/libpsn00b/libc/rand.s
@@ -0,0 +1,38 @@
+.set noreorder
+.set noat
+
+.section .text
+
+
+.global rand
+.type rand, @function
+rand:
+
+ la $at, _randseed
+ lw $v0, 0($at)
+ li $v1, 0x41c64e6d
+
+ multu $v0, $v1
+ mflo $v0
+ nop
+ addiu $v0, 12345
+ sw $v0, 0($at)
+
+ jr $ra
+ andi $v0, 0x7fff
+
+
+.global srand
+.type srand, @function
+srand:
+ la $at, _randseed
+ jr $ra
+ sw $a0, 0($at)
+
+
+.section .data
+
+.type _randseed, @object
+_randseed:
+ .word 1
+ \ No newline at end of file
diff --git a/libpsn00b/libc/readme.txt b/libpsn00b/libc/readme.txt
new file mode 100644
index 0000000..d7abf0a
--- /dev/null
+++ b/libpsn00b/libc/readme.txt
@@ -0,0 +1,48 @@
+Limited C standard library implementation, part of PSn00bSDK
+2019 Lameguy64 / Meido-Tek Productions
+
+ Some components were inherited from PSXSDK. This library covers only the
+most commonly used C functions, mainly most string and memory manipulation
+functions. Improvements to this library such as adding more standard C
+functions are welcome.
+
+ This library also contains the start code written in assembler which
+performs basic initialization such as clearing the bss section, setting the
+correct gp register value and initializing the heap for malloc.
+
+ The dynamic memory allocation functions featured in this library are of
+an original implementation and do not use the BIOS memory allocation functions
+as they are are reportedly prone to memory leakage and is even explained in
+the official library documents. The implementation employed uses a simple
+first-fit memory allocation logic.
+
+
+Library developer(s)/contributor(s):
+
+ Lameguy64
+
+
+Library header(s):
+
+ stdio.h
+ stdlib.h
+ string.h
+ strings.h
+ malloc.h
+
+
+Todo list:
+
+ * Current vsprintf/sprintf implementation from PSXSDK needs to be replaced
+ as it performs quite slow likely due to the unnecessary int64 arithmetic
+ performed on any integer value which the compiler has to emulate on the
+ R3000. A more efficient implementation that only uses int32 is much
+ preferred.
+
+ * Many of the string manipulation and memory fill functions in string.c
+ are yet to be replaced with more efficient assembly implementations.
+
+
+Changelog:
+
+ None thus far...
diff --git a/libpsn00b/libc/scanf.c b/libpsn00b/libc/scanf.c
new file mode 100644
index 0000000..b6d4510
--- /dev/null
+++ b/libpsn00b/libc/scanf.c
@@ -0,0 +1,427 @@
+// vsscanf
+// Programmed by Giuseppe Gatta, 2011
+// Inherited from PSXSDK C library
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+char libc_vsscanf_buf[512];
+char libc_vsscanf_allow[256];
+
+enum
+{
+ elem_skip_space = 1,
+};
+
+int libc_vsscanf_get_element(char *dst, const char *src, int flag, int s)
+{
+ int i;
+ const char *osrc = src;
+
+ if(flag & elem_skip_space)
+ {
+ while(*src == ' ')
+ src++;
+ }
+
+ for(i=0;i<s;i++)
+ {
+ if((flag & elem_skip_space) && *src == ' ')
+ break;
+
+ if(*src != 0)
+ *(dst++) = *(src++);
+ else
+ break;
+ }
+
+ *dst = 0;
+
+ return src - osrc;
+}
+
+enum
+{
+ scanf_s_char, scanf_s_short, scanf_s_int,
+ scanf_s_long,
+ scanf_s_long_long
+};
+
+int vsscanf(const char *str, const char *format, va_list ap)
+{
+ int fp = 0;
+ int sp = 0;
+ int conv = 0;
+ int sz = scanf_s_int; // size for numbers defaults to 32-bit
+ int i,x,y,z, h;
+ int suppress = 0;
+ int neg = 0;
+ int fsz = 512;
+ int def_fsz = 1;
+ int exspace = 0;
+ int alt = 0;
+ int r = 0;
+ int exit_loop=0;
+ int sp2,sp3;
+ char *ep;
+ long long buf;
+ double fbuf;
+
+
+ while(format[fp] && str[sp] && !exit_loop)
+ {
+ if(conv)
+ {
+ switch(format[fp])
+ {
+ case '%': // Percent, assignment does not occur
+ conv = 0;
+ break;
+
+ case 'h': // Halve size
+ sz--;
+ break;
+
+ case 'l': // Double size
+ sz++;
+ break;
+
+ case '*': // Suppress
+ suppress = 1;
+ break;
+
+ case ' ': // Explicit space
+ exspace = 1;
+ break;
+
+ case '#': // Alternate format
+ alt = 1;
+ break;
+
+ case '0' ... '9': // '0' ... '9' is a GNU C extension!
+ if(def_fsz)
+ {
+ def_fsz = 0;
+ fsz = 0;
+ }
+
+ fsz *= 10;
+ fsz+=format[fp]-'0';
+
+ if(fsz > 512)
+ fsz = 512; // 512 is the maximum.
+ break;
+
+ case '@': // Binary. Non-standard extension
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ buf = strtoll(libc_vsscanf_buf, &ep, 2);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, signed char*)) = (signed char)buf;break;
+ case scanf_s_short: *(va_arg(ap, short*)) = (short)buf; break;
+ case scanf_s_int: *(va_arg(ap, int*)) = (int)buf; break;
+ case scanf_s_long: *(va_arg(ap, long*)) = (long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, long long*)) = buf; break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 'D':
+ sz++;
+ case 'd': // Decimal
+ case 'u':
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ buf = strtoll(libc_vsscanf_buf, &ep, 10);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, signed char*)) = (signed char)buf;break;
+ case scanf_s_short: *(va_arg(ap, short*)) = (short)buf; break;
+ case scanf_s_int: *(va_arg(ap, int*)) = (int)buf; break;
+ case scanf_s_long: *(va_arg(ap, long*)) = (long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, long long*)) = buf; break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 's': // String
+ sp += libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+
+ if(!suppress)
+ {
+ strcpy(va_arg(ap, char*), libc_vsscanf_buf);
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 'c':
+ if(def_fsz)
+ fsz = 1;
+
+ sp += (i = libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], (exspace ? elem_skip_space : 0), fsz));
+ if(!suppress)
+ {
+ memcpy(va_arg(ap, char*), libc_vsscanf_buf, (fsz>i)?i:fsz);
+ r++;
+ }
+ break;
+
+ case 'n':
+ if(!suppress)
+ {
+ *(va_arg(ap, int*)) = sp;
+ r++;
+ }
+ break;
+
+ case 'p':
+ case 'x':
+ case 'X':
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ buf = strtoll(libc_vsscanf_buf, &ep, 16);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, unsigned char*)) = (unsigned char)buf; break;
+ case scanf_s_short: *(va_arg(ap, unsigned short*)) = (unsigned short)buf; break;
+ case scanf_s_int: *(va_arg(ap, unsigned int*)) = (unsigned int)buf; break;
+ case scanf_s_long: *(va_arg(ap, unsigned long*)) = (unsigned long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, unsigned long long*)) = (unsigned long long)buf; break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 'O':
+ sz++;
+ case 'o': // Octal integer
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ buf = strtoll(libc_vsscanf_buf, &ep, 8);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, unsigned char*)) = (unsigned char)buf;break;
+ case scanf_s_short: *(va_arg(ap, unsigned short*)) = (unsigned short)buf; break;
+ case scanf_s_int: *(va_arg(ap, unsigned int*)) = (unsigned int)buf;break;
+ case scanf_s_long: *(va_arg(ap, unsigned long*)) = (unsigned long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, unsigned long long*)) = (unsigned long long)buf;break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case 'i':
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+
+ if(libc_vsscanf_buf[0] == '0')
+ {
+ if(libc_vsscanf_buf[1] == 'x' || libc_vsscanf_buf[1] == 'X')
+ i = 16;
+ else
+ i = 8;
+ }
+ else
+ i = 10;
+
+ buf = strtoll(libc_vsscanf_buf, &ep, i);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char: *(va_arg(ap, signed char*)) = (signed char)buf; break;
+ case scanf_s_short: *(va_arg(ap, short*)) = (short)buf; break;
+ case scanf_s_int: *(va_arg(ap, int*)) = (int)buf; break;
+ case scanf_s_long: *(va_arg(ap, long*)) = (long)buf; break;
+ case scanf_s_long_long: *(va_arg(ap, long long*)) = (long long)buf; break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ case '[':
+ i=0;
+ x=0; // Exclusion?
+ h=0; // Hyphen?
+
+ fp++;
+ i++;
+
+ while(format[fp])
+ {
+ if(format[fp] == '^' && i==1)
+ {
+ memset(libc_vsscanf_allow, 1, 256);
+ x = 1;
+ fp++; i++; continue;
+ }
+
+ if(x)
+ {
+ if(format[fp] == ']' && i>=3)
+ break;
+ }
+ else
+ {
+ if(format[fp] == ']' && i>=2)
+ break;
+ }
+
+ if(format[fp] == '-')
+ {
+ if(format[fp+1] != ']')
+ y = 1;
+ else
+ libc_vsscanf_allow['-'] = x^1;
+ }
+ else
+ {
+ if(y == 1)
+ {
+ if(format[fp] < format[fp-2])
+ libc_vsscanf_allow[format[fp]] = x^1;
+ else
+ for(z = format[fp-2]; z <= format[fp]; z++)
+ libc_vsscanf_allow[z] = x^1;
+
+ y = 0;
+
+ //printf("%s all chars from %c to %c\n", x?"Excluding":"Including",format[fp-2], format[fp]);
+ }
+ else
+ libc_vsscanf_allow[format[fp]] = x^1;
+ }
+
+ fp++;
+ i++;
+ }
+
+// Now as we know what our character set is, let's get data from the string
+ /* puts("Character set:");
+
+ for(y=0;y<16;y++)
+ {
+ for(x=0;x<16;x++)
+ if(libc_vsscanf_allow[(y*16) + x])
+ putchar((y*16)+x);
+ else
+ putchar('*');
+
+ putchar('\n');
+ }
+ */
+ i = 0;
+
+ while(libc_vsscanf_allow[str[sp]] && i<512)
+ libc_vsscanf_buf[i++] = str[sp++];
+
+ libc_vsscanf_buf[i] = 0;
+
+ if(!suppress)
+ {
+ strcpy(va_arg(ap, char*), libc_vsscanf_buf);
+ r++;
+ }
+ break;
+
+ case 'f': // Floating point number
+ libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz);
+ fbuf = strtod(libc_vsscanf_buf, &ep);
+ sp += ep - libc_vsscanf_buf;
+
+ if(!suppress)
+ {
+ switch(sz)
+ {
+ case scanf_s_char:
+ case scanf_s_short:
+ case scanf_s_int:
+ *(va_arg(ap, float*)) = (float)fbuf;
+ break;
+
+ case scanf_s_long:
+ case scanf_s_long_long:
+ *(va_arg(ap, double*)) = fbuf;
+ break;
+ }
+ r++;
+ }
+
+ conv = 0;
+ break;
+
+ }
+ }
+ else
+ {
+ if(format[fp] == '%')
+ {
+ conv = 1;
+ neg = 0;
+ suppress = 0;
+ sz = scanf_s_int;
+ fsz = 512;
+ def_fsz = 1;
+ exspace = 0;
+ alt = 0;
+ bzero(libc_vsscanf_allow, 256);
+ //chset = 0;
+ }
+ else if(format[fp] != ' ')
+ {
+ if(format[fp] != str[sp])
+ exit_loop=1;
+
+ sp++;
+ }
+
+ }
+
+ fp++;
+ }
+
+ return r;
+}
+
+int sscanf(const char *str, const char *fmt, ...)
+{
+ int r;
+ va_list ap;
+
+ va_start(ap, fmt);
+ r = vsscanf(str, fmt, ap);
+
+ va_end(ap);
+ return r;
+}
diff --git a/libpsn00b/libc/start.s b/libpsn00b/libc/start.s
new file mode 100644
index 0000000..c9b529b
--- /dev/null
+++ b/libpsn00b/libc/start.s
@@ -0,0 +1,39 @@
+# Start function!
+# This is essentially the entrypoint of the PS-EXE
+
+.set noreorder
+
+.section .text
+
+.global _start
+.type _start, @function
+_start:
+
+ addiu $sp, -32
+ sw $ra, 28($sp)
+
+ la $gp, _gp # Very important to set!
+
+ la $a0, .bss # What are the CORRECT symbols for BSS start and end?
+ la $a1, _end
+.clear_bss:
+ sb $0 , 0($a0)
+ blt $a0, $a1, .clear_bss
+ addiu $a0, 1
+
+ la $a0, _end+4 # Initialize heap for malloc (does not use BIOS maalloc)
+ li $a1, 1572864
+ jal InitHeap
+ nop
+
+ move $a0, $0 # No support for arguments for now
+ move $a1, $0
+
+ jal main
+ nop
+
+ lw $ra, 28($sp)
+ addiu $sp, 32
+ jr $ra
+ nop
+ \ No newline at end of file
diff --git a/libpsn00b/libc/string.c b/libpsn00b/libc/string.c
new file mode 100644
index 0000000..4943877
--- /dev/null
+++ b/libpsn00b/libc/string.c
@@ -0,0 +1,301 @@
+/*
+ * string.c
+ *
+ * Inherited from PSXSDK C library
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+
+int tolower(int chr)
+{
+ return (chr >='A' && chr<='Z') ? (chr + 32) : (chr);
+}
+
+int toupper(int chr)
+{
+ return (chr >='a' && chr<='z') ? (chr - 32) : (chr);
+}
+
+// Need to be replaced with MIPS assembler equivalents
+
+void *memchr(void *s , int c , int n)
+{
+ while(n--)
+ {
+ if(*((unsigned char*)s) == (unsigned char)c)
+ return s;
+
+ s++;
+ }
+
+ return NULL;
+}
+
+char *strncpy(char *dst, const char *src, int len)
+{
+ char *odst=dst;
+
+ while(*src && len)
+ {
+ *(dst++) = *(src++);
+ len--;
+ }
+
+ if(len)*dst = 0;
+
+ return odst;
+}
+
+char *strcpy(char *dst, const char *src)
+{
+ char *odst = dst;
+
+ while(*(dst++) = *(src++));
+ return odst;
+}
+
+char *strncat(char *s, const char *append, int len)
+{
+ char *o=s;
+
+ while(*s)
+ s++;
+
+ strncpy(s, append, len);
+
+ return o;
+}
+
+int strlen(const char *str)
+{
+ int i = 0;
+ while(*(str++))i++;
+ return i;
+}
+
+char *strchr(const char *s, int c)
+{
+ int x;
+
+ for(x = 0; x <= strlen(s); x++)
+ if(s[x] == c) return (char*)&s[x];
+
+ return NULL;
+}
+
+char *strrchr(const char *s, int c)
+{
+ int x;
+
+ for(x = strlen(s); x>=0; x--)
+ if(s[x] == c) return (char*)&s[x];
+
+ return NULL;
+}
+
+char *strpbrk(const char *s, const char *charset)
+{
+ int x,y;
+
+ for(x = 0; x < strlen(s); x++)
+ for(y = 0; y < strlen(charset); y++)
+ if(s[x] == charset[y]) return (char*)&s[x];
+
+ return NULL;
+}
+
+char *strstr(const char *big, const char *little)
+{
+ int ls = strlen(little);
+ int bs = strlen(big);
+ int x;
+
+ if(ls == 0)
+ return (char*)big;
+
+ if(ls > bs)
+ return NULL;
+
+ for(x = 0; x <= bs-ls; x++)
+ if(memcmp(little, &big[x], ls) == 0)
+ return (char*)&big[x];
+
+ return NULL;
+}
+
+int strcmp(const char *s1, const char *s2)
+{
+ while(*s1 && *s2 && (*s1 == *s2))
+ {
+ s1++;
+ s2++;
+ }
+
+ return *s1-*s2;
+}
+
+int strncmp(const char *s1, const char *s2, int len)
+{
+ int p = 0;
+
+ while(*s1 && *s2 && (*s1 == *s2) && p<len)
+ {
+ p++;
+
+ if(p<len)
+ {
+ s1++;
+ s2++;
+ }
+ }
+
+ return *s1-*s2;
+}
+
+// Requires a malloc implementation
+char *strdup(const char *str)
+{
+ char *ns = (void*)malloc(strlen(str) + 1);
+
+ if(ns == NULL)
+ return NULL;
+
+ strcpy(ns, str);
+ return ns;
+}
+
+char *strndup(const char *str, int len)
+{
+ int n=strlen(str);
+ char *ns = (void*)malloc((n+1)>len?len:(n+1));
+
+ if(ns == NULL)
+ return NULL;
+
+ strncpy(ns, str, (n+1)>len?len:(n+1));
+ return ns;
+}
+
+long long strtoll(const char *nptr, char **endptr, int base)
+{
+ int r = 0;
+ int t = 0;
+ int n = 0;
+
+ if(*nptr == '-')
+ {
+ nptr++;
+ n = 1;
+ }
+
+ if(base == 0)
+ if(*nptr == '0')
+ base = 8;
+ else
+ base = 10;
+
+ if(!(base >= 2 && base <= 36))
+ return 0;
+
+ if(base == 16 && *nptr == '0')
+ {
+ if(*(nptr+1) == 'x' || *(nptr+1) == 'X')
+ nptr+=2;
+ }
+
+ while(*nptr)
+ {
+ switch(*nptr)
+ {
+ case '0'...'9':
+ t = *nptr - '0';
+ break;
+ case 'a' ... 'z':
+ t = (*nptr - 'a') + 10;
+ break;
+ case 'A' ... 'Z':
+ t = (*nptr - 'A') + 10;
+ break;
+ default:
+ t = 1000;
+ break;
+ }
+
+ if(t>=base)
+ break;
+
+ r*=base;
+ r+=t;
+ nptr++;
+ }
+
+ if(endptr)*endptr = (char*)nptr;
+ return n?-r:r;
+}
+
+long strtol(const char *nptr, char **endptr, int base)
+{
+ return (long)strtoll(nptr, endptr, base);
+}
+
+double strtod(const char *nptr, char **endptr)
+{
+ char strbuf[64];
+ int x = 0;
+ int y;
+ double i=0, d=0;
+ int s=1;
+
+ if(*nptr == '-')
+ {
+ nptr++;
+ s=-1;
+ }
+
+ while(*nptr >= '0' && *nptr <= '9' && x < 18)
+ strbuf[x++] = *(nptr++);
+
+ strbuf[x] = 0;
+
+ i = (double)strtoll(strbuf, NULL, 10);
+
+ if(*nptr == '.')
+ {
+ nptr++;
+ x = 0;
+
+ while(*nptr >= '0' && *nptr <= '9' && x < 7)
+ strbuf[x++] = *(nptr++);
+
+ strbuf[x] = 0;
+
+ if(endptr != NULL) *endptr = (char*)nptr;
+
+ y=1;
+
+ for(x=0;x<strlen(strbuf);x++)
+ y*=10;
+
+ d = (double)strtoll(strbuf, NULL, 10);
+ d /= y;
+ }
+ else
+ {
+ if(endptr != NULL)
+ *endptr = (char*)nptr;
+ }
+
+ return (i + d)*s;
+}
+
+long double strtold(const char *nptr, char **endptr)
+{
+ return (long double)strtod(nptr, endptr);
+}
+
+float strtof(const char *nptr, char **endptr)
+{
+ return (float)strtod(nptr, endptr);
+}