diff options
| author | John Wilbert M. Villamor <lameguy64@gmail.com> | 2019-04-06 10:11:07 +0800 |
|---|---|---|
| committer | John Wilbert M. Villamor <lameguy64@gmail.com> | 2019-04-06 10:11:07 +0800 |
| commit | f3e040230772f978540a71aea43dfde200992922 (patch) | |
| tree | bd8ca31b72dd01e24980b073854e263589530f56 /libpsn00b/libc | |
| download | psn00bsdk-f3e040230772f978540a71aea43dfde200992922.tar.gz | |
First commit
Diffstat (limited to 'libpsn00b/libc')
| -rw-r--r-- | libpsn00b/libc/makefile | 38 | ||||
| -rw-r--r-- | libpsn00b/libc/malloc.s | 216 | ||||
| -rw-r--r-- | libpsn00b/libc/memcmp.s | 31 | ||||
| -rw-r--r-- | libpsn00b/libc/memcpy.s | 28 | ||||
| -rw-r--r-- | libpsn00b/libc/memmove.s | 29 | ||||
| -rw-r--r-- | libpsn00b/libc/memset.s | 25 | ||||
| -rw-r--r-- | libpsn00b/libc/printf.c | 788 | ||||
| -rw-r--r-- | libpsn00b/libc/rand.s | 38 | ||||
| -rw-r--r-- | libpsn00b/libc/readme.txt | 48 | ||||
| -rw-r--r-- | libpsn00b/libc/scanf.c | 427 | ||||
| -rw-r--r-- | libpsn00b/libc/start.s | 39 | ||||
| -rw-r--r-- | libpsn00b/libc/string.c | 301 |
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); +} |
