From 75f0bde8cc0d5a987714174d800989e9485117ad Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 7 Jun 2023 14:41:02 +0300 Subject: [PATCH 1/3] * Start of my experiments with a dynamically linked libc --- .gitignore | 2 + apps/CommonMakefile | 6 +- apps/List/Makefile | 74 +- crt/lib/crt1.o | Bin 1024 -> 1000 bytes crt/lib/crti.o | Bin 436 -> 476 bytes crt/lib/crtn.o | Bin 436 -> 476 bytes crt/lib/libnanoshell.a | Bin 254314 -> 214070 bytes crt/src/a_assert.c | 10 +- crt/src/entry.c | 7 - crt2/Makefile | 96 +++ crt2/README.md | 31 + crt2/crt1.ld | 7 + crt2/include/LICENSE.md | 8 + crt2/include/README | 5 + crt2/include/alloca.h | 9 + crt2/include/assert.h | 17 + crt2/include/ctype.h | 25 + crt2/include/dirent.h | 30 + crt2/include/errno.h | 16 + crt2/include/fcntl.h | 9 + crt2/include/inttypes.h | 10 + crt2/include/iso646.h | 16 + crt2/include/limits.h | 26 + crt2/include/math.h | 9 + crt2/include/nanoshell/dirent_types.h | 44 ++ crt2/include/nanoshell/error_nums.h | 45 ++ crt2/include/nanoshell/graphics.h | 45 ++ crt2/include/nanoshell/graphics_types.h | 329 ++++++++ crt2/include/nanoshell/keyboard.h | 113 +++ crt2/include/nanoshell/lock.h | 12 + crt2/include/nanoshell/lock_types.h | 15 + crt2/include/nanoshell/mman_types.h | 29 + crt2/include/nanoshell/nanoshell.h | 171 +++++ crt2/include/nanoshell/setjmp_types.h | 16 + crt2/include/nanoshell/stdio_types.h | 22 + crt2/include/nanoshell/stdlib_types.h | 15 + crt2/include/nanoshell/time_types.h | 73 ++ crt2/include/nanoshell/types.h | 740 ++++++++++++++++++ crt2/include/nanoshell/unistd_types.h | 72 ++ crt2/include/nsstandard.h | 4 + crt2/include/nsstructs.h | 4 + crt2/include/setjmp.h | 16 + crt2/include/stdalign.h | 10 + crt2/include/stdarg.h | 11 + crt2/include/stdbool.h | 11 + crt2/include/stddef.h | 16 + crt2/include/stdint.h | 107 +++ crt2/include/stdio.h | 59 ++ crt2/include/stdlib.h | 63 ++ crt2/include/stdnoreturn.h | 6 + crt2/include/string.h | 39 + crt2/include/sys/mman.h | 12 + crt2/include/sys/time.h | 9 + crt2/include/sys/types.h | 25 + crt2/include/time.h | 22 + crt2/include/unistd.h | 18 + crt2/lib_i386.ld | 3 + crt2/src/a_assert.c | 28 + crt2/src/a_cc.c | 17 + crt2/src/a_env.c | 18 + crt2/src/a_error.c | 57 ++ crt2/src/a_file.c | 829 +++++++++++++++++++++ crt2/src/a_math.c | 60 ++ crt2/src/a_mem.c | 638 ++++++++++++++++ crt2/src/a_printf.c | 566 ++++++++++++++ crt2/src/a_sort.c | 65 ++ crt2/src/a_string.c | 949 ++++++++++++++++++++++++ crt2/src/a_time.c | 141 ++++ crt2/src/a_ver.c | 30 + crt2/src/a_video.c | 59 ++ crt2/src/a_window.c | 17 + crt2/src/calldefs.h | 606 +++++++++++++++ crt2/src/calls.c | 62 ++ crt2/src/crt0.asm | 94 +++ crt2/src/crtinternal.h | 32 + crt2/src/crtlib.h | 309 ++++++++ crt2/src/entry.c | 62 ++ crt2/src/math.asm | 1 + crt2/src/zstub.c | 2 + fs/Bin/List.nse | Bin 59480 -> 12312 bytes src/elf.c | 51 +- 81 files changed, 7228 insertions(+), 54 deletions(-) create mode 100644 crt2/Makefile create mode 100644 crt2/README.md create mode 100644 crt2/crt1.ld create mode 100644 crt2/include/LICENSE.md create mode 100644 crt2/include/README create mode 100644 crt2/include/alloca.h create mode 100644 crt2/include/assert.h create mode 100644 crt2/include/ctype.h create mode 100644 crt2/include/dirent.h create mode 100644 crt2/include/errno.h create mode 100644 crt2/include/fcntl.h create mode 100644 crt2/include/inttypes.h create mode 100644 crt2/include/iso646.h create mode 100644 crt2/include/limits.h create mode 100644 crt2/include/math.h create mode 100644 crt2/include/nanoshell/dirent_types.h create mode 100644 crt2/include/nanoshell/error_nums.h create mode 100644 crt2/include/nanoshell/graphics.h create mode 100644 crt2/include/nanoshell/graphics_types.h create mode 100644 crt2/include/nanoshell/keyboard.h create mode 100644 crt2/include/nanoshell/lock.h create mode 100644 crt2/include/nanoshell/lock_types.h create mode 100644 crt2/include/nanoshell/mman_types.h create mode 100644 crt2/include/nanoshell/nanoshell.h create mode 100644 crt2/include/nanoshell/setjmp_types.h create mode 100644 crt2/include/nanoshell/stdio_types.h create mode 100644 crt2/include/nanoshell/stdlib_types.h create mode 100644 crt2/include/nanoshell/time_types.h create mode 100644 crt2/include/nanoshell/types.h create mode 100644 crt2/include/nanoshell/unistd_types.h create mode 100644 crt2/include/nsstandard.h create mode 100644 crt2/include/nsstructs.h create mode 100644 crt2/include/setjmp.h create mode 100644 crt2/include/stdalign.h create mode 100644 crt2/include/stdarg.h create mode 100644 crt2/include/stdbool.h create mode 100644 crt2/include/stddef.h create mode 100644 crt2/include/stdint.h create mode 100644 crt2/include/stdio.h create mode 100644 crt2/include/stdlib.h create mode 100644 crt2/include/stdnoreturn.h create mode 100644 crt2/include/string.h create mode 100644 crt2/include/sys/mman.h create mode 100644 crt2/include/sys/time.h create mode 100644 crt2/include/sys/types.h create mode 100644 crt2/include/time.h create mode 100644 crt2/include/unistd.h create mode 100644 crt2/lib_i386.ld create mode 100644 crt2/src/a_assert.c create mode 100644 crt2/src/a_cc.c create mode 100644 crt2/src/a_env.c create mode 100644 crt2/src/a_error.c create mode 100644 crt2/src/a_file.c create mode 100644 crt2/src/a_math.c create mode 100644 crt2/src/a_mem.c create mode 100644 crt2/src/a_printf.c create mode 100644 crt2/src/a_sort.c create mode 100644 crt2/src/a_string.c create mode 100644 crt2/src/a_time.c create mode 100644 crt2/src/a_ver.c create mode 100644 crt2/src/a_video.c create mode 100644 crt2/src/a_window.c create mode 100644 crt2/src/calldefs.h create mode 100644 crt2/src/calls.c create mode 100644 crt2/src/crt0.asm create mode 100644 crt2/src/crtinternal.h create mode 100644 crt2/src/crtlib.h create mode 100644 crt2/src/entry.c create mode 100644 crt2/src/math.asm create mode 100644 crt2/src/zstub.c diff --git a/.gitignore b/.gitignore index fe51ca2c..421c5399 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,8 @@ fs/Bin/Chess.nse fs/Tests/*.png fs/User/* +crt2/build/* +crt2/lib/* crt/build_libnanoshell crt/build_crt1 crt/crt1.o diff --git a/apps/CommonMakefile b/apps/CommonMakefile index 4c2dbeb2..30225ed5 100644 --- a/apps/CommonMakefile +++ b/apps/CommonMakefile @@ -14,8 +14,10 @@ LD=ld AS=nasm STRIP=strip +USERLDFLAGS ?= + CFLAGS=-I $(INC_DIR) -I $(LII_DIR) -ffreestanding -target i686-elf -O2 -fno-exceptions -Wall -Wextra -std=c99 -mno-sse2 $(USERCFLAGS) -LDFLAGS=-T link.ld -g -nostdlib -zmax-page-size=0x1000 +LDFLAGS=-T link.ld -g -nostdlib -zmax-page-size=0x1000 $(USERLDFLAGS) ASFLAGS=-f elf32 # Compile the application @@ -58,7 +60,7 @@ endif $(APP_TARGET): $(APP_O_FILES) $(LIB_O_FILES) $(RESOURCE_COMP_OBJ) @echo "linking: $@" @echo $(APP_O_FILES) $(LIB_O_FILES) $(RESOURCE_COMP_OBJ) - @$(LD) $(LDFLAGS) -o $@ $^ $(LGCC) + $(LD) $(LDFLAGS) -o $@ $^ $(LGCC) # Build Library $(BLD_DIR)/%.asm.o: $(LIS_DIR)/%.asm diff --git a/apps/List/Makefile b/apps/List/Makefile index 236bb47a..929697b5 100644 --- a/apps/List/Makefile +++ b/apps/List/Makefile @@ -5,4 +5,76 @@ SRC_DIR=src APP_C_FILES=$(shell find $(SRC_DIR) -type f -name '*.c') APP_S_FILES=$(shell find $(SRC_DIR) -type f -name '*.asm') -include ../CommonMakefile +USERLDFLAGS = -L ../../crt2/lib -lnanoshell -lcrts --dynamic-linker nanoshelllinker + +# NanoShell Operating System + +# Common Makefile. This is supposed to be included by built in applications, and not spawned by itself.. + +INC_DIR=include +BLD_DIR=build +LIS_DIR=../../crt/src +LII_DIR=../../crt/include + +LGCC=../../tools/libgcc-i686.a + +CC=clang +LD=ld +AS=nasm +STRIP=strip + +USERLDFLAGS ?= + +CFLAGS=-I $(INC_DIR) -I $(LII_DIR) -ffreestanding -target i686-elf -O2 -fno-exceptions -Wall -Wextra -std=c99 -mno-sse2 $(USERCFLAGS) +LDFLAGS=-T link.ld -g -nostdlib -zmax-page-size=0x1000 $(USERLDFLAGS) +ASFLAGS=-f elf32 + +# Compile the application +APP_TARGET=$(APPLICATION_NAME).nse + +APP_O_FILES=$(patsubst $(SRC_DIR)/%,$(BLD_DIR)/%.o,$(APP_C_FILES) $(APP_S_FILES)) + +all: application + +clean: + rm -rf $(BLD_DIR)/* + rm -f $(APP_TARGET) + +application: $(APP_TARGET) + +# Build Resource File + +RC = ../../tools/rc + +RESOURCE=resource.rc +RESOURCE_COMPILED=$(BLD_DIR)/rescomp.asm +RESOURCE_COMP_OBJ=$(BLD_DIR)/rescomp.o + +ifeq ("$(wildcard $(RESOURCE))", "") +RESOURCE_COMP_OBJ := +else +$(RESOURCE_COMP_OBJ): $(RESOURCE_COMPILED) + @mkdir -p $(dir $@) + @echo "assembling resource file" + @$(AS) $(ASFLAGS) -o $@ $^ + +$(RESOURCE_COMPILED): $(RESOURCE) + @mkdir -p $(dir $@) + @$(RC) $< $@ /S +endif + +$(APP_TARGET): $(APP_O_FILES) $(RESOURCE_COMP_OBJ) + @echo "linking: $@" + @echo $(APP_O_FILES) $(RESOURCE_COMP_OBJ) + $(LD) $(LDFLAGS) -o $@ $^ $(LGCC) + +# Build Source +$(BLD_DIR)/%.asm.o: $(SRC_DIR)/%.asm + @mkdir -p $(dir $@) + @echo "assembling: $<" + @$(AS) $(ASFLAGS) -o $@ $^ + +$(BLD_DIR)/%.c.o: $(SRC_DIR)/%.c + @mkdir -p $(dir $@) + @echo "compiling: $<" + @$(CC) $(CFLAGS) -c $< -o $@ diff --git a/crt/lib/crt1.o b/crt/lib/crt1.o index bc5815163a1550d1795bbbdd4599c2ac3d09eb93..d9401c2c19eccf56055378919330e81bd54bc01b 100644 GIT binary patch delta 161 zcmZqRc)>nFf$_pd#e7D?Vg?2VAs_|;0R~2f8VD24Z$#lUFif6&k+FEPFO#`C0}D`! ziHCuK6GZbfFo*zYMkWRZDJZQ55*L`fifJBW&SX#KU`Bz-bD5P{FEBDN%$|IdSy^HU OP_Z^p5ext&zyJVY@e>~a delta 162 zcmaFC-oPh($ diff --git a/crt/lib/crti.o b/crt/lib/crti.o index 6addee9f7c04ee8288ef2b2b2fcf21928e388c62..1edeb88167b897386d7df6a5bd0c836dbbdde802 100644 GIT binary patch delta 179 zcmdnOe2000fhIcxBLhQdQfXdEsX}s2VqUsJS!z*nW`3T6p^2V>o`J4mDOl2Q;zeOb zy@|E@ym~n~Wx4T*DJei@=@VBwiZd{nF)%PN12GKL02ywR85#W<)g}irDzip`B!GNj nO9dc<+2?;gkZJ&829Qb+V1rN~$AEzykYs|gK~&7-xs2ifRg58n delta 173 zcmcb^yoGs!fiD{aBLhQnPGVlVLRo52ab|v=f|;Iyo`J4mX;NukNvV;7MoD6Nv3`)N zkE>&_YrL6(fqq(MUSf_W0|ro-n5#c=m4m1TP=*;vwgN&0K{z@o&W#< diff --git a/crt/lib/crtn.o b/crt/lib/crtn.o index 6addee9f7c04ee8288ef2b2b2fcf21928e388c62..1edeb88167b897386d7df6a5bd0c836dbbdde802 100644 GIT binary patch delta 179 zcmdnOe2000fhIcxBLhQdQfXdEsX}s2VqUsJS!z*nW`3T6p^2V>o`J4mDOl2Q;zeOb zy@|E@ym~n~Wx4T*DJei@=@VBwiZd{nF)%PN12GKL02ywR85#W<)g}irDzip`B!GNj nO9dc<+2?;gkZJ&829Qb+V1rN~$AEzykYs|gK~&7-xs2ifRg58n delta 173 zcmcb^yoGs!fiD{aBLhQnPGVlVLRo52ab|v=f|;Iyo`J4mX;NukNvV;7MoD6Nv3`)N zkE>&_YrL6(fqq(MUSf_W0|ro-n5#c=m4m1TP=*;vwgN&0K{z@o&W#< diff --git a/crt/lib/libnanoshell.a b/crt/lib/libnanoshell.a index 7cdfcb987ee637b451c4e0772e7a4d41ef6e16a4..2fb2340d3f3f06cfdc9894fd4c3d50b6dec19775 100644 GIT binary patch literal 214070 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%0Am2>11bU209AmQ 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 JS(0P&x^C- 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 z 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 diff --git a/crt/src/a_assert.c b/crt/src/a_assert.c index b8754d89..1a52480b 100644 --- a/crt/src/a_assert.c +++ b/crt/src/a_assert.c @@ -11,10 +11,18 @@ #include "crtlib.h" #include "crtinternal.h" +__attribute__((noreturn)) +void abort() +{ + // TODO + *((uint32_t*)0xFFFFFFF4) = 0xFFFFFFFF; + while (true); +} + void OnAssertionFail(const char *cond_msg, const char *file, int line) { LogMsg("ASSERTION FAILED!"); LogMsg("The assertion \"%s\" failed at %s:%d.", cond_msg, file, line); LogMsg("The program will now exit."); - exit(1); + abort(); } diff --git a/crt/src/entry.c b/crt/src/entry.c index 20cff68a..f09fa960 100644 --- a/crt/src/entry.c +++ b/crt/src/entry.c @@ -11,13 +11,6 @@ #include "crtlib.h" #include "crtinternal.h" -__attribute__((noreturn)) -void abort() -{ - *((uint32_t*)0xFFFFFFF4) = 0xFFFFFFFF; - while (true); -} - __attribute__((noreturn)) void exit (int number); int main(int argc, char** argv); diff --git a/crt2/Makefile b/crt2/Makefile new file mode 100644 index 00000000..444ee60a --- /dev/null +++ b/crt2/Makefile @@ -0,0 +1,96 @@ +# crt2/Makefile +# Copyright (C) 2023 iProgramInCpp +# Makefile for the NanoShell C Runtime Library + +CC = clang +LD = ld +AS = nasm +AR = ar + +# Description: +# libnanoshell - The shared library that implements most of nanoshell's libc. +# libnanoentry - The static library that implements the entry point of all programs. + +# Note: Libnanoshell also includes libgcc. + +# Objects included with libnanoshell. +LIBN_C_FILES = \ + src/a_assert.c \ + src/a_cc.c \ + src/a_env.c \ + src/a_error.c \ + src/a_file.c \ + src/a_math.c \ + src/a_mem.c \ + src/a_printf.c \ + src/a_sort.c \ + src/a_string.c \ + src/a_time.c \ + src/a_ver.c \ + src/a_video.c \ + src/a_window.c \ + src/calls.c +LIBN_ASM_FILES = \ + src/math.asm + +# Objects included with libnanoentry. +LIBE_C_FILES = \ + src/entry.c +LIBE_ASM_FILES = \ + src/crt0.asm + +CRT_STUB = src/zstub.c + +LGCC=../tools/libgcc-i686.a + +LIBN_O_FILES = $(patsubst src/%,build/libnanoshell/%.o,$(LIBN_C_FILES) $(LIBN_ASM_FILES)) +LIBE_O_FILES = $(patsubst src/%,build/libnanoentry/%.o,$(LIBE_C_FILES) $(LIBE_ASM_FILES)) + +CFLAGS=-I include/ -I src/ -ffreestanding -target i686-elf -g -fno-exceptions -Wall -Wextra -std=c99 -mno-sse -mno-sse2 -fpic +ASFLAGS=-f elf32 +ARFLAGS=rcs + +LIBN_TARGET = lib/libnanoshell.so +LIBE_TARGET = lib/libnanoentry.a + +all: libnanoshell libnanoentry + +clean: + rm -rf build + rm -rf lib + +# Build LibNanoshell +libnanoshell: $(LIBN_O_FILES) + @mkdir -p $(dir $@) + @echo "[LibNanoShell] Linking" + @mkdir -p $(dir $(LIBN_TARGET)) + @$(LD) -shared -T lib_i386.ld -g -nostdlib -zmax-page-size=0x1000 -o $(LIBN_TARGET) $(LIBN_O_FILES) $(LGCC) + +# Build LibNanoentry +libnanoentry: $(LIBE_O_FILES) + @mkdir -p $(dir $@) + @echo "[LibNanoEntry] Archiving" + @$(AR) $(ARFLAGS) $(LIBE_TARGET) $^ + +# Build LibNanoshell object files +build/libnanoshell/%.asm.o: src/%.asm + @mkdir -p $(dir $@) + @echo "[LibNanoShell] Assembling $<" + @$(AS) $(ASFLAGS) -o $@ $^ + +build/libnanoshell/%.c.o: src/%.c + @mkdir -p $(dir $@) + @echo "[LibNanoShell] Compiling $<" + @$(CC) $(CFLAGS) -c $< -o $@ + +# Build Libnanoentry object files. +build/libnanoentry/%.c.o: src/%.c + @mkdir -p $(dir $@) + @echo "[LibNanoEntry] Compiling $<" + @$(CC) $(CFLAGS) -c $< -o $@ + +build/libnanoentry/%.asm.o: src/%.asm + @mkdir -p $(dir $@) + @echo "[LibNanoEntry] Assembling $<" + @$(AS) $(ASFLAGS) -o $@ $^ + diff --git a/crt2/README.md b/crt2/README.md new file mode 100644 index 00000000..716f11ee --- /dev/null +++ b/crt2/README.md @@ -0,0 +1,31 @@ +### The NanoShell C Standard Library + +#### File structure + +The `include` directory contains include headers. Make +sure to use the `-nostdinc` switch when compiling an +application. + +The `src` directory contains all NanoShell C Standard +Library code. Please note that new files must be added +manually to the Makefile in the directory this README +resides in. + +The provided makefile has a couple functions: + +`make all`: Compiles the crti.o and crtn.o stubs, the +crt1.o entry point, and the libnanoshell.a archive for +the contents of the NanoShell C library. + +`make update`: Does the same as `male all` and copies +the libraries into system root +(`git repo root/fs/User/Library`). + +`make updinc`: Updates the include files from `crt/` +to `git repo root/fs/User/Include`. + +**Note**: The `User/` directory is entirely optional +and not at all used by NanoShell itself. It's required +to use TCC, though, a port of which is provided within +`apps/Tcc`. It is NanoShell's counterpart to the `usr` +directory. diff --git a/crt2/crt1.ld b/crt2/crt1.ld new file mode 100644 index 00000000..f3f3fee5 --- /dev/null +++ b/crt2/crt1.ld @@ -0,0 +1,7 @@ +/** + * This linker script is purely to let the linker know + * that we're trying to get an elf32-i386 object out of + * crt1. + */ +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386) \ No newline at end of file diff --git a/crt2/include/LICENSE.md b/crt2/include/LICENSE.md new file mode 100644 index 00000000..09c59c30 --- /dev/null +++ b/crt2/include/LICENSE.md @@ -0,0 +1,8 @@ +Copyright 2022 mintsuki and contributors. + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/crt2/include/README b/crt2/include/README new file mode 100644 index 00000000..fc0a0a4c --- /dev/null +++ b/crt2/include/README @@ -0,0 +1,5 @@ +Source: https://github.com/mintsuki/freestanding-headers/tree/74f400838200f0b2968241155302cd1261e22b5f + +This is a collection of freestanding C headers, for use with GCC or Clang. + + diff --git a/crt2/include/alloca.h b/crt2/include/alloca.h new file mode 100644 index 00000000..53d02c1d --- /dev/null +++ b/crt2/include/alloca.h @@ -0,0 +1,9 @@ +// alloca.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _ALLOCA__H +#define _ALLOCA__H + +#define alloca __builtin_alloca + +#endif//_ALLOCA__H diff --git a/crt2/include/assert.h b/crt2/include/assert.h new file mode 100644 index 00000000..5d61264c --- /dev/null +++ b/crt2/include/assert.h @@ -0,0 +1,17 @@ +// assert.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _ASSERT__H +#define _ASSERT__H + +void OnAssertionFail(const char *cond_msg, const char *file, int line); +#define assert(cond) do { if (!(cond)) OnAssertionFail(#cond, __FILE__, __LINE__); } while (0) +#define ASSERT assert + +#ifdef STATIC_ASSERT +#undef STATIC_ASSERT +#endif + +#define STATIC_ASSERT _Static_assert + +#endif//_ASSERT__H diff --git a/crt2/include/ctype.h b/crt2/include/ctype.h new file mode 100644 index 00000000..1ecb88a1 --- /dev/null +++ b/crt2/include/ctype.h @@ -0,0 +1,25 @@ +// ctype.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _CTYPE__H +#define _CTYPE__H + +// Character classification +int isalnum(int c); +int isalpha(int c); +int isascii(int c); +int isblank(int c); +int iscntrl(int c); +int isdigit(int c); +int isgraph(int c); +int islower(int c); +int isprint(int c); +int isspace(int c); +int isupper(int c); +int isxdigit(int c); + +// Character space mapping functions +int toupper(int c); +int tolower(int c); + +#endif//_CTYPE__H diff --git a/crt2/include/dirent.h b/crt2/include/dirent.h new file mode 100644 index 00000000..a7e821d6 --- /dev/null +++ b/crt2/include/dirent.h @@ -0,0 +1,30 @@ +// dirent.h +// Copyright (C) 2023 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _DIRENT_H +#define _DIRENT_H + +#include + +// NanoShell specifics that have yet to have POSIX names assigned to them. (TODO) +int FiOpenDir (const char* pFileName); +int FiCloseDir (int dd); +int FiReadDir (DirEnt* pDirEnt, int dd); +int FiSeekDir (int dd, int loc); +int FiRewindDir(int dd); +int FiTellDir (int dd); +int FiStatAt (int dd, const char*pfn, StatResult* pres); +int FiStat (const char*pfn, StatResult* pres); +int FiChDir (const char*pfn); +const char* FiGetCwd(); +const char* ErrNoStr(int errno); + +// Standard C functions +DIR* opendir(const char* dirname); +int closedir(DIR *dirp); +void rewinddir(DIR* dirp); +int telldir(DIR* dirp); +void seekdir(DIR* dirp, int told); +struct dirent* readdir(DIR *dirp); + +#endif//_DIRENT_H \ No newline at end of file diff --git a/crt2/include/errno.h b/crt2/include/errno.h new file mode 100644 index 00000000..b57efde6 --- /dev/null +++ b/crt2/include/errno.h @@ -0,0 +1,16 @@ +// errno.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __ERRNO_H +#define __ERRNO_H + +#include + +int seterrno(int en); +int geterrno(int en); +int* geterrnoptr(); +#define errno (*geterrnoptr()) + +void perror(const char* fmt, ...); + +#endif//__ERRNO_H \ No newline at end of file diff --git a/crt2/include/fcntl.h b/crt2/include/fcntl.h new file mode 100644 index 00000000..afa943ef --- /dev/null +++ b/crt2/include/fcntl.h @@ -0,0 +1,9 @@ +// fcntl.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __FCNTL_H +#define __FCNTL_H + +//TODO + +#endif//__FCNTL_H \ No newline at end of file diff --git a/crt2/include/inttypes.h b/crt2/include/inttypes.h new file mode 100644 index 00000000..4851957c --- /dev/null +++ b/crt2/include/inttypes.h @@ -0,0 +1,10 @@ +// inttypes.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __INTTYPES_H +#define __INTTYPES_H + +#include +typedef signed long ssize_t; + +#endif//__INTTYPES_H \ No newline at end of file diff --git a/crt2/include/iso646.h b/crt2/include/iso646.h new file mode 100644 index 00000000..599705ee --- /dev/null +++ b/crt2/include/iso646.h @@ -0,0 +1,16 @@ +#ifndef _ISO646_H +#define _ISO646_H 1 + +#define and && +#define and_eq &= +#define bitand & +#define bitor | +#define compl ~ +#define not ! +#define not_eq != +#define or || +#define or_eq |= +#define xor ^ +#define xor_eq ^= + +#endif diff --git a/crt2/include/limits.h b/crt2/include/limits.h new file mode 100644 index 00000000..e80f3f2a --- /dev/null +++ b/crt2/include/limits.h @@ -0,0 +1,26 @@ +// limits.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __LIMITS__H +#define __LIMITS__H + +#define CHAR_BIT (8) +#define SCHAR_MIN (-128) +#define SCHAR_MAX (127) +#define UCHAR_MAX (255) +#define CHAR_MIN (-128) +#define CHAR_MAX (127) +#define SHRT_MIN (-32768) +#define SHRT_MAX (32767) +#define USHRT_MAX (65535) +#define INT_MIN (-2147483648) +#define INT_MAX (2147483647) +#define UINT_MAX (4294967295U) +#define LONG_MIN INT_MIN +#define LONG_MAX INT_MAX +#define ULONG_MAX UINT_MAX +#define LLONG_MIN (-9223372036854775808LL) +#define LLONG_MAX (9223372036854775807LL) +#define ULLONG_MAX (18446744073709551615ULL) + +#endif//__LIMITS__H \ No newline at end of file diff --git a/crt2/include/math.h b/crt2/include/math.h new file mode 100644 index 00000000..a1d2e708 --- /dev/null +++ b/crt2/include/math.h @@ -0,0 +1,9 @@ +// math.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __MATH_H +#define __MATH_H + +//TODO + +#endif//__MATH_H \ No newline at end of file diff --git a/crt2/include/nanoshell/dirent_types.h b/crt2/include/nanoshell/dirent_types.h new file mode 100644 index 00000000..690b77d2 --- /dev/null +++ b/crt2/include/nanoshell/dirent_types.h @@ -0,0 +1,44 @@ +// nanoshell/dirent_types.h +// Copyright (C) 2023 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _DIRENT_TYPES_H +#define _DIRENT_TYPES_H + +// matches the FILE_TYPE enum (unistd_types.h) +#define DT_UNKNOWN (0) +#define DT_REG (1) +#define DT_CHR (2) +#define DT_BLK (3) +#define DT_FIFO (4) +#define DT_LNK (5) +#define DT_DIR (8) +#define DT_SOCK (32) // fantasy value. Will never be set unless we implement unix sockets. + +typedef struct DirEntS +{ + char m_name[128]; //+nullterm, so 127 concrete chars + uint32_t m_inode; //device specific + uint32_t m_type; +} +DirEnt; + +struct dirent +{ + ino_t d_ino; // Inode number + off_t d_off; // Offset in the file (i.e. the value of telldir() for this dirent) + uint16_t d_reclen; // Record length + uint8_t d_type; // Optional type of file, may be DT_UNKNOWN if not supported. + char d_name[128]; // Null terminated file name. The size matches that of the DirEnt structure's +}; + +typedef struct dirent dirent; + +typedef struct +{ + int m_DirHandle; + DirEnt m_NDirEnt; // nanoshell dirent + dirent m_PDirEnt; // posix dirent +} +DIR; // pointer to a DIR returned by opendir + +#endif//_DIRENT_TYPES_H \ No newline at end of file diff --git a/crt2/include/nanoshell/error_nums.h b/crt2/include/nanoshell/error_nums.h new file mode 100644 index 00000000..6c24556c --- /dev/null +++ b/crt2/include/nanoshell/error_nums.h @@ -0,0 +1,45 @@ +// nanoshell/error_nums.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __NANOSHELL_ERROR_NUMS_H +#define __NANOSHELL_ERROR_NUMS_H + +// use with the negative prefix +enum +{ + ENOTHING, // No Error + EACCES, // Permission denied + EEXIST, // File exists + EINTR, // Interrupted system call + EINVAL, // Invalid argument + EIO, // I/O error + EISDIR, // Is a directory + ELOOP, // Too many symbolic links + EMFILE, // Too many open files + ENAMETOOLONG, // File or path name too long + ENFILE, // Too many open files in system + ENOENT, // No such file or directory + ENOSR, // Out of stream resources + ENOSPC, // No space left on device + ENOTDIR, // Not a directory + ENXIO, // No such device or address + EOVERFLOW, // Value too large for defined data type + EROFS, // Read only file system + EAGAIN, // No more processes + ENOMEM, // Not enough memory + ETXTBUSY, // Text file busy + EBADF, // Bad file descriptor + ESPIPE, // Illegal seek + EIEIO, // Computer bought the farm + ENOTSUP, // Operation not supported + EXDEV, // Cross device operation not supported + EBUSY, // Resource is busy + ENOTEMPTY, // Directory is not empty + ENOTTY, // Invalid input/output control request + ERANGE, // Range error + EDOM, // Domain error + ECOUNT, +}; + + +#endif//__NANOSHELL_ERROR_NUMS_H diff --git a/crt2/include/nanoshell/graphics.h b/crt2/include/nanoshell/graphics.h new file mode 100644 index 00000000..4185a0b5 --- /dev/null +++ b/crt2/include/nanoshell/graphics.h @@ -0,0 +1,45 @@ +// nanoshell/graphics.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _NANOSHELL_GRAPHICS__H +#define _NANOSHELL_GRAPHICS__H + +#include + +int GetScreenSizeX(); +int GetScreenSizeY(); +int GetWidth(Rectangle* rect); +int GetHeight(Rectangle* rect); +void VidPlotPixel(unsigned x, unsigned y, unsigned color); +void VidFillScreen(unsigned color); +void VidDrawVLine(unsigned color, int top, int bottom, int x); +void VidDrawHLine(unsigned color, int left, int right, int y); +void VidDrawLine(unsigned p, int x1, int y1, int x2, int y2); +void VidSetFont(unsigned fontType); +void VidPlotChar (char c, unsigned ox, unsigned oy, unsigned colorFg, unsigned colorBg /*=0xFFFFFFFF*/); +void VidBlitImage(Image* pImage, int x, int y); +void VidBlitImageResize(Image* pImage, int x, int y, int w, int h); +void VidTextOut(const char* pText, unsigned ox, unsigned oy, unsigned colorFg, unsigned colorBg /*=0xFFFFFFFF*/); +void VidTextOutInternal(const char* pText, unsigned ox, unsigned oy, unsigned colorFg, unsigned colorBg, bool doNotActuallyDraw, int* widthx, int* heightx); +void VidDrawText(const char* pText, Rectangle rect, unsigned drawFlags, unsigned colorFg, unsigned colorBg); +void VidShiftScreen (int amount); +void VidFillRect(unsigned color, int left, int top, int right, int bottom); +void VidDrawRect(unsigned color, int left, int top, int right, int bottom); +void VidFillRectangle(unsigned color, Rectangle rect); +void VidFillRectHGradient(unsigned colorL, unsigned colorR, int left, int top, int right, int bottom); +void VidFillRectVGradient(unsigned colorU, unsigned colorD, int left, int top, int right, int bottom); +void VidDrawRectangle(unsigned color, Rectangle rect); +void SetMousePos (int pX, int pY); +void RenderIcon(int type, int x, int y); +void RenderIconOutline(int type, int x, int y, uint32_t color); +void RenderIconForceSize(int type, int x, int y, int size); +void RenderIconForceSizeOutline(int type, int x, int y, int size, uint32_t color); + +bool RectangleContains(Rectangle *r, Point *p); +bool RectangleOverlap (Rectangle *r1, Rectangle *r2); + +unsigned VidReadPixel(unsigned x, unsigned y); + +VBEData* VidSetVbeData (VBEData* pData); + +#endif//_NANOSHELL_GRAPHICS__H diff --git a/crt2/include/nanoshell/graphics_types.h b/crt2/include/nanoshell/graphics_types.h new file mode 100644 index 00000000..9b4e462f --- /dev/null +++ b/crt2/include/nanoshell/graphics_types.h @@ -0,0 +1,329 @@ +// nanoshell/graphics_types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _NANOSHELL_GRAPHICS_TYPES_H +#define _NANOSHELL_GRAPHICS_TYPES_H + +#define TRANSPARENT 0xFFFFFFFF + +#define FLAGS_TOO(flags, color) (flags | (color & 0XFFFFFF)) + +#define TEXT_RENDER_TRANSPARENT 0xFFFFFFFF +#define TEXT_RENDER_BOLD 0x01000000 + +#define RECT(rect,x,y,w,h) do {\ + rect.left = x, rect.top = y, rect.right = x+w, rect.bottom = y+h;\ +} while (0) + +typedef struct +{ + int left, top, right, bottom; +} +Rectangle; + +typedef struct +{ + int x, y; +} +Point; + +typedef struct +{ + short width, height; + const uint32_t *framebuffer; +} +Image; + +enum +{ + ICON_NULL, + ICON_CABINET, + ICON_CHIP, + ICON_CHIP_SQ, + ICON_COMPUTER, + ICON_COMPUTER_SHUTDOWN, + ICON_DESKTOP, + ICON_DRAW, + ICON_EARTH, + ICON_ERROR, + ICON_EXECUTE_FILE, + ICON_FILE, + ICON_FILES, + ICON_FOLDER, + ICON_FOLDER_BLANK, + ICON_FOLDER_MOVE, + ICON_FOLDER_PARENT, + ICON_FOLDER16_CLOSED, + ICON_FOLDER16_OPEN, + ICON_GLOBE, + ICON_GO, + ICON_HAND, + ICON_HELP, + ICON_INFO, + ICON_KEYBOARD, + ICON_KEYBOARD2, + ICON_LAPTOP, + ICON_NOTES, + ICON_PAINT, + ICON_SERIAL, + ICON_STOP, + ICON_TEXT_FILE, + ICON_WARNING, + ICON_NANOSHELL_LETTERS, + ICON_NANOSHELL_LETTERS16, + ICON_NANOSHELL, + ICON_NANOSHELL16, + ICON_BOMB, + ICON_BOMB_SPIKEY, + ICON_FILE16, + ICON_TEXT_FILE16, + ICON_EXECUTE_FILE16, + ICON_FOLDER_PARENT16, + //icons V1.1 + ICON_FOLDER_SETTINGS, + ICON_CABINET16, + ICON_COMPUTER16, + ICON_COMMAND, + ICON_COMMAND16, + ICON_ERROR16, + //icons V1.2 + ICON_LOCK, + ICON_DIRECTIONS, + ICON_CERTIFICATE, + ICON_FILE_WRITE, + ICON_SCRAP_FILE, + ICON_SCRAP_FILE16, + ICON_RESMON, + ICON_BILLBOARD, + ICON_FILE_CSCRIPT, + ICON_FILE_CSCRIPT16, + ICON_FILE_CLICK, + ICON_KEYS, + ICON_RESTRICTED, + ICON_HOME, + ICON_HOME16, + ICON_ADAPTER, + ICON_CLOCK, + ICON_CLOCK16, + //icons V1.3 + ICON_APPLICATION, + ICON_APPLICATION16, + ICON_TASKBAR, + ICON_APP_DEMO, + ICON_COMPUTER_FLAT, + ICON_CALCULATOR, + ICON_CALCULATOR16, + ICON_DESKTOP2, + ICON_MOUSE, + //Icons V1.31 + ICON_AMBULANCE, + //icons V1.32 + ICON_FONTS, + ICON_FONTS16, + //icons V1.33 + ICON_RESMON16, + ICON_NOTES16, + ICON_FILE_NANO, + //icons V1.34 + ICON_CLOCK_EMPTY,//Special case which draws more stuff + //icons V1.35 + ICON_RUN, + ICON_RUN16, + //icons V1.4 + ICON_DEVTOOL, + ICON_DEVTOOL_FILE, + ICON_HEX_EDIT, + ICON_CHAIN, + ICON_CHAIN16, + ICON_DEVTOOL16, + ICON_TODO, + ICON_FOLDER_DOCU, + ICON_DLGEDIT, + ICON_DESK_SETTINGS, + ICON_SHUTDOWN, + ICON_NOTEPAD, + ICON_FILE_MKDOWN, + ICON_FILE_MKDOWN16, + ICON_COMPUTER_PANIC, + ICON_EXPERIMENT, + ICON_GRAPH, + ICON_CABINET_COMBINE, + ICON_REMOTE, + ICON_CABINET_OLD, + //icons V1.5 + ICON_DEVICE_CHAR, + ICON_DEVICE_BLOCK, + ICON_HARD_DRIVE, + ICON_HARD_DRIVE_MOUNT, + ICON_WINDOW, + ICON_WINDOW_SNAP, + ICON_WINDOW_OVERLAP, + ICON_SWEEP_SMILE, + ICON_SWEEP_CLICK, + ICON_SWEEP_DEAD, + ICON_SWEEP_CARET, + ICON_DLGEDIT16, + ICON_BOMB_SPIKEY16, + ICON_MAGNIFY, + ICON_MAGNIFY16, + ICON_TAR_ARCHIVE, + ICON_SYSMON, + ICON_SYSMON16, + ICON_COMPUTER_SHUTDOWN16, + ICON_EXIT, + ICON_KEY, + ICON_KEYB_REP_SPEED, + ICON_KEYB_REP_DELAY, + ICON_MONITOR, + //icons V1.6 + ICON_FILE_INI, + ICON_WMENU, + ICON_WMENU16, + ICON_FILE_IMAGE, + ICON_FILE_IMAGE16, + ICON_FILE_LOG, + ICON_STICKY_NOTES, + ICON_STICKY_NOTES16, + ICON_NOTE_YELLOW, + ICON_NOTE_BLUE, + ICON_NOTE_GREEN, + ICON_NOTE_WHITE, + ICON_FOLDER_OPEN, + //icons V1.61 + ICON_EXPERIMENT2, + ICON_FLOPPY, ICON_ACTION_SAVE = ICON_FLOPPY, + ICON_ACTION_SAVE16, + ICON_ACTION_OPEN, + ICON_ACTION_OPEN16, + ICON_PLUS, + //icons V1.7 + ICON_PASTE, + ICON_PASTE16, + ICON_DELETE, + ICON_DELETE16, + ICON_COPY, + ICON_COPY16, + ICON_BACK, + ICON_BACK16, + ICON_FORWARD, + ICON_FORWARD16, + ICON_UNDO, + ICON_UNDO16, + ICON_REDO, + ICON_REDO16, + ICON_FILE_SEARCH, + ICON_FILE_SEARCH16, + ICON_FILE_PROPERTIES, + ICON_FILE_PROPERTIES16, + ICON_PROPERTIES, + ICON_PROPERTIES16, + ICON_WHATS_THIS, + ICON_WHATS_THIS16, + ICON_VIEW_ICON, + ICON_VIEW_ICON16, + ICON_VIEW_LIST, + ICON_VIEW_LIST16, + ICON_VIEW_TABLE, + ICON_VIEW_TABLE16, + ICON_SORT_ALPHA, + ICON_SORT_ALPHA16, + ICON_FORM, + ICON_FORM16, + ICON_JOURNAL, + ICON_JOURNAL16, + ICON_PACKAGER, + ICON_PACKAGER16, + //icons V1.71 + ICON_BOX_CHECK, + ICON_BOX_UNCHECK, + ICON_FOLDER_SETTINGS16, + //icons V1.8 + ICON_MINIMIZE, + ICON_MAXIMIZE, + ICON_RESTORE, + ICON_CLOSE, + ICON_ARROW_UP, + ICON_ARROW_DOWN, + ICON_ARROW_LEFT, + ICON_ARROW_RIGHT, + ICON_PIPE, + ICON_PIPE16, + ICON_VB_CURSOR, + ICON_VB_SELECT, + ICON_VB_TEXT, + ICON_VB_TEXT_CEN, + ICON_VB_INPUT_1LINE, + ICON_VB_INPUT_MLINE, + ICON_VB_CHECKBOX, + ICON_VB_SURR_RECT, + ICON_VB_BUTTON, + ICON_CLIPBOARD, + ICON_PAINT2, + ICON_FILE_BROKEN, + ICON_STOP_BLACK, + ICON_STOP_SMALL, + ICON_PAUSE_BLACK, + ICON_PAUSE_SMALL, + ICON_PLAY_BLACK, + ICON_PLAY_SMALL, + ICON_BROWSE_SMALL, + ICON_COUNT +}; + +enum CURSORTYPE +{ + CURSOR_DEFAULT, + CURSOR_WAIT, + CURSOR_IBEAM, + CURSOR_CROSS, + CURSOR_PENCIL, + CURSOR_COUNT, +}; + +enum +{ + FONT_TAMSYN_REGULAR, + FONT_TAMSYN_BOLD, + FONT_PAPERM, + FONT_FAMISANS, + FONT_BASIC, + FONT_GLCD, + FONT_TAMSYN_MED_REGULAR, + FONT_TAMSYN_MED_BOLD, + FONT_TAMSYN_SMALL_REGULAR, + FONT_TAMSYN_SMALL_BOLD, + //FONT_BIGTEST, + //FONT_BIGTEST2, + FONT_LAST, +}; + +typedef struct +{ + bool m_available; //if the vbe display is available + unsigned m_width, m_height, m_pitch;//bytes per row + int m_bitdepth; //bits per pixel, only values we support: 0=8, 1=16, 2=32 + bool m_dirty; //useful if the framebuffer won't directly be pushed to the screen + union { + uint32_t* m_framebuffer32; //for ease of addressing + uint16_t* m_framebuffer16; + uint8_t * m_framebuffer8; + }; + int m_pitch32, m_pitch16; //uint32_t's and uint16_t's per row. + Rectangle m_clipRect; +} +VBEData; + +typedef struct +{ + uint16_t width, height; + int16_t leftOffs, topOffs; + const uint32_t* bitmap; + bool m_transparency;//optimization + + bool m_resizeMode; + uint16_t boundsWidth, boundsHeight; + uint16_t mouseLockX, mouseLockY; +} +Cursor; + +#endif//_NANOSHELL_GRAPHICS_TYPES_H diff --git a/crt2/include/nanoshell/keyboard.h b/crt2/include/nanoshell/keyboard.h new file mode 100644 index 00000000..a325f0d2 --- /dev/null +++ b/crt2/include/nanoshell/keyboard.h @@ -0,0 +1,113 @@ +// nanoshell/keyboard.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _NANOSHELL_KEYBOARD__H +#define _NANOSHELL_KEYBOARD__H +#define _NANOSHELL_KEYBOARD__H + +#define KEY_UNDEFINED_0 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_HYPHEN KEY_MINUS +#define KEY_EQUALS 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_A 0x1e +#define KEY_B 0x30 +#define KEY_C 0x2e +#define KEY_D 0x20 +#define KEY_E 0x12 +#define KEY_F 0x21 +#define KEY_G 0x22 +#define KEY_H 0x23 +#define KEY_I 0x17 +#define KEY_J 0x24 +#define KEY_K 0x25 +#define KEY_L 0x26 +#define KEY_M 0x32 +#define KEY_N 0x31 +#define KEY_O 0x18 +#define KEY_P 0x19 +#define KEY_Q 0x10 +#define KEY_R 0x13 +#define KEY_S 0x1f +#define KEY_T 0x14 +#define KEY_U 0x16 +#define KEY_V 0x2f +#define KEY_W 0x11 +#define KEY_X 0x2d +#define KEY_Y 0x15 +#define KEY_Z 0x2c +#define KEY_BRACKET_LEFT 0x1a +#define KEY_BRACKET_RIGHT 0x1b +#define KEY_ENTER 0x1c +#define KEY_CONTROL 0x1d +#define KEY_CTRL KEY_CONTROL +#define KEY_SEMICOLON 0x27 +#define KEY_APOSTROPHE 0x28 +#define KEY_BACKTICK 0x29 +#define KEY_LSHIFT 0x2a +#define KEY_BACKSLASH 0x2b +#define KEY_COMMA 0x33 +#define KEY_DOT 0x34 +#define KEY_SLASH 0x35 +#define KEY_RSHIFT 0x36 +#define KEY_PRINTSCREEN 0x37 +#define KEY_ALT 0x38 +#define KEY_SPACE 0x39 +#define KEY_CAPSLOCK 0x3a +#define KEY_F1 0x3b +#define KEY_F2 0x3c +#define KEY_F3 0x3d +#define KEY_F4 0x3e +#define KEY_F5 0x3f +#define KEY_F6 0x40 +#define KEY_F7 0x41 +#define KEY_F8 0x42 +#define KEY_F9 0x43 +#define KEY_F10 0x44 +#define KEY_NUMLOCK 0x45 +#define KEY_SCROLLLOCK 0x46 +#define KEY_HOME 0x47 +#define KEY_ARROW_UP 0x48 +#define KEY_PAGEUP 0x49 +#define KEY_NUMPAD_MINUS 0x4a +#define KEY_NUMPAD_HYPHEN KEY_NUMPAD_MINUS +#define KEY_ARROW_LEFT 0x4b +#define KEY_LEFT KEY_ARROW_LEFT +#define KEY_UNDEFINED_4C 0x4c +#define KEY_ARROW_RIGHT 0x4d +#define KEY_RIGHT KEY_ARROW_RIGHT +#define KEY_NUMPAD_PLUS 0x4e +#define KEY_END 0x4f +#define KEY_ARROW_DOWN 0x50 +#define KEY_DOWN KEY_ARROW_DOWN +#define KEY_PAGEDOWN 0x51 +#define KEY_INSERT 0x52 +#define KEY_DELETE 0x53 +#define KEY_UNDEFINED_54 0x54 +#define KEY_UNDEFINED_55 0x55 +#define KEY_UNDEFINED_56 0x56 +#define KEY_F11 0x57 +#define KEY_F12 0x58 +#define KEY_UP KEY_ARROW_UP +#define KEY_MENU 0x5D + +#define SCANCODE_RELEASE 0x80 + +typedef uint8_t KeyState; +#define KEY_PRESSED ((KeyState) 1) +#define KEY_HELD ((KeyState) 2) +#define KEY_RELEASED ((KeyState) 0) + +#endif//_NANOSHELL_KEYBOARD__H diff --git a/crt2/include/nanoshell/lock.h b/crt2/include/nanoshell/lock.h new file mode 100644 index 00000000..57da06d6 --- /dev/null +++ b/crt2/include/nanoshell/lock.h @@ -0,0 +1,12 @@ +// nanoshell/lock.h +// Copyright (C) 2023 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _NANOSHELL_LOCK__H +#define _NANOSHELL_LOCK__H + +#include + +void LockAcquire (SafeLock *pLock); +void LockFree (SafeLock *pLock); + +#endif//_NANOSHELL_LOCK__H diff --git a/crt2/include/nanoshell/lock_types.h b/crt2/include/nanoshell/lock_types.h new file mode 100644 index 00000000..bc9b292a --- /dev/null +++ b/crt2/include/nanoshell/lock_types.h @@ -0,0 +1,15 @@ +// nanoshell/lock_types.h +// Copyright (C) 2023 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _NANOSHELL_LOCKTYPES__H +#define _NANOSHELL_LOCKTYPES__H + +typedef struct +{ + volatile bool m_held; + volatile void* m_task_owning_it; + volatile void* m_return_addr; +} +SafeLock; + +#endif//_NANOSHELL_LOCKTYPES__H diff --git a/crt2/include/nanoshell/mman_types.h b/crt2/include/nanoshell/mman_types.h new file mode 100644 index 00000000..4dc43bb0 --- /dev/null +++ b/crt2/include/nanoshell/mman_types.h @@ -0,0 +1,29 @@ +// nanoshell/mman_types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __MMAN_TYPES_H +#define __MMAN_TYPES_H + +#include + +// mmap() flags +#define PROT_NONE (0 << 0) +#define PROT_READ (1 << 0) +#define PROT_WRITE (1 << 1) +#define PROT_EXEC (1 << 2) //not applicable here + +#define MAP_FAILED ((void*) -1) //not NULL + +#define MAP_FILE (0 << 0) //retroactive, TODO +#define MAP_SHARED (1 << 0) //means changes in the mmapped region will be written back to the file on unmap/close +#define MAP_PRIVATE (1 << 1) //means changes won't be committed back to the source file +#define MAP_FIXED (1 << 4) //fixed memory mapping means that we really want it at 'addr'. +#define MAP_ANONYMOUS (1 << 5) //anonymous mapping, means that there's no file backing this mapping :) +#define MAP_ANON (1 << 5) //synonymous with "MAP_ANONYMOUS" +#define MAP_NORESERVE (0 << 0) //don't reserve swap space, irrelevent here + +#define MAP_DONTREPLACE (1 << 30) //don't clobber preexisting fixed mappings there. Used with MAP_FIXED to create... +#define MAP_FIXED_NOREPLACE (MAP_DONTREPLACE | MAP_FIXED) + + +#endif//__MMAN_TYPES_H \ No newline at end of file diff --git a/crt2/include/nanoshell/nanoshell.h b/crt2/include/nanoshell/nanoshell.h new file mode 100644 index 00000000..ac4f999b --- /dev/null +++ b/crt2/include/nanoshell/nanoshell.h @@ -0,0 +1,171 @@ +// nanoshell/nanoshell.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library + +// This non-portable include file includes everything from NanoShell. + +#ifndef _NANOSHELL___H +#define _NANOSHELL___H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NanoShell specifics +#include +#include + +// Threading +void sleep(int ms); //not actually standard I don't think +void exit (int errcode); + +// Assertion + + +// Optimized memory operations to word width + +//NOTE: size must be 4 byte aligned!! +void ZeroMemory (void* bufptr1, size_t size); +void fmemcpy32 (void* restrict dest, const void* restrict src, size_t size); +void* fast_memset(void* bufptr, uint8_t val, size_t size); + +// File management + +// Graphics Interface + +// Window API +Window* CreateWindow (const char* title, int xPos, int yPos, int xSize, int ySize, WindowProc proc, int flags); +bool HandleMessages(Window* pWindow); +void DefaultWindowProc (Window* pWindow, int messageType, int parm1, int parm2); +void DestroyWindow (Window* pWindow); +int MessageBox (Window* pWindow, const char* pText, const char* pCaption, uint32_t type); +int AddControl(Window* pWindow, int type, Rectangle rect, const char* text, int comboID, int p1, int p2); +void SetScrollBarMin (Window *pWindow, int comboID, int min); +void SetScrollBarMax (Window *pWindow, int comboID, int max); +void SetScrollBarPos (Window *pWindow, int comboID, int pos); +int GetScrollBarPos (Window *pWindow, int comboID); +void AddElementToList (Window* pWindow, int comboID, const char* pText, int optionalIcon); +const char* GetElementStringFromList (Window* pWindow, int comboID, int index); +void RemoveElementFromList (Window* pWindow, int comboID, int elemIndex); +void SetListItemText(Window* pWindow, int comboID, int index, int icon, const char * pText); +void ResetList (Window* pWindow, int comboID); +void SetLabelText (Window *pWindow, int comboID, const char* pText); +void AddMenuBarItem (Window* pWindow, int menuBarControlId, int comboIdTo, int comboIdAs, const char* pText); +void SetHugeLabelText (Window *pWindow, int comboID, const char* pText); +void SetTextInputText(Window* pWindow, int comboID, const char* pText); +void SetWindowIcon (Window* pWindow, int icon); +void SetIcon (Window* pWindow, int comboID, int icon); +void SetWindowTitle(Window* pWindow, const char* pTitle); +void RegisterEvent(Window* pWindow, short evType, int parm1, int parm2); +void RegisterEventInsideWndProc(Window* pWindow, short evType, int parm1, int parm2); +int AddControlEx(Window* pWindow, int type, int anchor_mode, Rectangle rect, const char* text, int comboID, int p1, int p2); +bool TextInputQueryDirtyFlag(Window* pWindow, int comboID); +void TextInputClearDirtyFlag(Window* pWindow, int comboID); +const char* TextInputGetRawText(Window* pWindow, int comboID); +bool CheckboxGetChecked(Window* pWindow, int comboID); +void CheckboxSetChecked(Window* pWindow, int comboID, bool checked); +void SetTextInputText(Window* pWindow, int comboID, const char* pText); +void ShellAbout (const char* pText, int icon); +void RequestRepaint (Window *pWindow); +void RequestRepaintNew (Window *pWindow); +void PopupWindow (Window* pWindow, const char* newWindowTitle, int newWindowX, int newWindowY, int newWindowW, int newWindowH, WindowProc newWindowProc, int newFlags); +void PopupWindowEx(Window* pWindow, const char* newWindowTitle, int newWindowX, int newWindowY, int newWindowW, int newWindowH, WindowProc newWindowProc, int newFlags, void* pData); +uint32_t ColorInputBox (Window *pWindow, const char *pPrompt, const char *pCaption); +uint32_t GetThemingParameter (int type); +void SetThemingParameter (int type, uint32_t parm); +void SetWidgetEventHandler(Window *pWindow, int comboID, WidgetEventHandler handler); +WidgetEventHandler GetWidgetEventHandler(Window* pWindow, int comboID); +// The input boxes that return strings return a kernel memory region. Use MmKernelFree() instead of free() to free it. +char* InputBox (Window *pWindow, const char *pPrompt, const char *pCaption, const char *pDefaultText); +char* FilePickerBox(Window* pWindow, const char* pPrompt, const char* pCaption, const char* pDefaultText); +void CallControlCallback(Window * pWindow, int comboID, int event, int parm1, int parm2); +void TextInputSetMode (Window *pWindow, int comboID, int mode); +int GetScrollBarMin (Window *pWindow, int comboID); +int GetScrollBarMax (Window *pWindow, int comboID); +int GetSelectedIndexList (Window* pWindow, int comboID); +void SetSelectedIndexList (Window* pWindow, int comboID, int index); +int GetSelectedIndexTable(Window* pWindow, int comboID); +void SetSelectedIndexTable(Window* pWindow, int comboID, int selectedIndex); +int GetScrollTable(Window* pWindow, int comboID); +void SetScrollTable(Window* pWindow, int comboID, int scroll); +void AddTableRow(Window* pWindow, int comboID, const char* pText[], int optionalIcon); +void AddTableColumn(Window* pWindow, int comboID, const char* pText, int width); +bool GetRowStringsFromTable(Window* pWindow, int comboID, int index, const char * output[]); +void RemoveRowFromTable(Window* pWindow, int comboID, int elementIndex); +void ResetTable(Window* pWindow, int comboID); +const char* GetWindowTitle(Window* pWindow); +void* GetWindowData(Window* pWindow); +void SetWindowData(Window* pWindow, void* pData); +void GetWindowRect(Window* pWindow, Rectangle* pRectOut); +void ChangeCursor(Window* pWindow, int cursorID); +void SetImageCtlMode(Window* pWindow, int comboID, int mode); +void CallWindowCallbackAndControls(Window* pWindow, int eventType, int parm1, int parm2); +int GetWindowFlags(Window * pWindow); +void SetWindowFlags(Window * pWindow, int flags); +int AddTimer(Window* pWindow, int frequencyMs, int eventFired); +void DisarmTimer(Window* pWindow, int timerID); +void ChangeTimer(Window* pWindow, int newFrequencyMs /* = -1 */, int newEventFired /* = -1 */); +int UploadCursor(Image* pImage, int xOff, int yOff); +void ReleaseCursor(int cursorID); +Image* GetIconImage(int iconType, int size /* = -1 */); +Resource* GetResource(int resID); +Point GetMousePos(); +void SetImageCtlMode(Window* pWindow, int comboID, int mode); +void SetImageCtlColor(Window* pWindow, int comboID, uint32_t color); +void SetImageCtlCurrentImage(Window* pWindow, int comboID, Image* pImage); +Image* GetImageCtlCurrentImage(Window* pWindow, int comboID); +void ImageCtlZoomToFill(Window* pWindow, int comboID); +void SetControlDisabled(Window* pWindow, int comboID, bool flag); +void SetControlFocused (Window* pWindow, int comboID, bool flag); +void SetControlVisible (Window* pWindow, int comboID, bool flag); +void VidSetClipRect(Rectangle* rect); +void DrawEdge(Rectangle rect, int style, unsigned bg); +void DrawArrow(Rectangle rect, eArrowType arrowType, int flags, unsigned color); +void ProgBarSetProgress(Window* pWindow, int comboID, int prog); +void ProgBarSetMaxProg(Window* pWindow, int comboID, int max_prog); +void TextInputSetFont(Window *pWindow, int comboID, unsigned font); +void TextInputRequestCommand(Window *pWindow, int comboID, int command, void* parm); +void ComboBoxAddItem(Window* pWindow, int comboID, const char* item, int itemID, int iconID); +int ComboBoxGetSelectedItemID(Window* pWindow, int comboID); +void ComboBoxSetSelectedItemID(Window* pWindow, int comboID, int itemID); +void ComboBoxClearItems(Window* pWindow, int comboID); +bool IsControlFocused(Window* pWindow, int comboID); +bool IsControlDisabled(Window* pWindow, int comboID); +KeyState KbGetKeyState(unsigned char keycode); +Window* SpawnMenu(Window* pParentWindow, WindowMenu *root, int x, int y); + +// Internal C Compiler +int CcRunCCode(const char* pCode, int length); + +// NanoShell Versioning +int NsGetVersion (); +const char* GetVersionString(); + +// Shell +int ShellExecute (const char *pCommand); //for instance, "e " +int ShellExecuteResource(const char *pResourceID); //for instance, shell:stuff + +// Errors +int SetErrorNumber(int en); +int GetErrorNumber(); +int* GetErrorNumberPointer(); +#define ErrorNumber (*GetErrorNumberPointer()) + +// Kernel memory resource management. +// NOTE: Improper management of kernel memory resources will cause a leak that +// will persist over the rest of the OS' runtime, so use carefully!!!! +void* MmKernelAllocate(size_t sz); + +//note: Do not feed this function addresses from malloc(). +void MmKernelFree(void *pData); + + +#endif//_NANOSHELL___H \ No newline at end of file diff --git a/crt2/include/nanoshell/setjmp_types.h b/crt2/include/nanoshell/setjmp_types.h new file mode 100644 index 00000000..ec2ecaeb --- /dev/null +++ b/crt2/include/nanoshell/setjmp_types.h @@ -0,0 +1,16 @@ +// nanoshell/setjmp_types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _SETJMP_TYPES_H +#define _SETJMP_TYPES_H +// https://github.com/jezze/subc +typedef struct +{ + void *esp, *eax, *ebp; + void *ebx, *esi, *edi; +} +JumpBufferTag; + +typedef JumpBufferTag JumpBuffer[1], jmp_buf[1]; + +#endif//_SETJMP_TYPES_H diff --git a/crt2/include/nanoshell/stdio_types.h b/crt2/include/nanoshell/stdio_types.h new file mode 100644 index 00000000..04de4f5a --- /dev/null +++ b/crt2/include/nanoshell/stdio_types.h @@ -0,0 +1,22 @@ +// nanoshell/stdio_types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library + +#ifndef __NANOSHELL_STDIO_TYPES_H +#define __NANOSHELL_STDIO_TYPES_H + +#include + +#define EOF (-1) + +typedef struct +{ + int fd; + uint8_t ungetc_buf[4]; + int ungetc_buf_sz; + bool eof; + bool error; +} +FILE; + +#endif//__NANOSHELL_STDIO_TYPES_H diff --git a/crt2/include/nanoshell/stdlib_types.h b/crt2/include/nanoshell/stdlib_types.h new file mode 100644 index 00000000..91ff151c --- /dev/null +++ b/crt2/include/nanoshell/stdlib_types.h @@ -0,0 +1,15 @@ +// nanoshell/stdlib_types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library + +#ifndef __NANOSHELL_STDLIB_TYPES_H +#define __NANOSHELL_STDLIB_TYPES_H + +#include + +#define RAND_MAX (0x7FFFFFFF) + +typedef int(*ComparisonFunc) (const void*, const void*); +typedef int(*ComparisonReentrantFunc) (const void*, const void*, void*); + +#endif//__NANOSHELL_STDLIB_TYPES_H \ No newline at end of file diff --git a/crt2/include/nanoshell/time_types.h b/crt2/include/nanoshell/time_types.h new file mode 100644 index 00000000..f8c1c587 --- /dev/null +++ b/crt2/include/nanoshell/time_types.h @@ -0,0 +1,73 @@ +// nanoshell/time_types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _TIME_TYPES_H +#define _TIME_TYPES_H + +#include + +typedef long time_t; + +struct timespec +{ + time_t tv_sec; + long tv_nsec; +}; + +struct timeval +{ + time_t tv_sec; + long tv_usec; +}; + +struct tm +{ + int tm_sec; // Seconds after the minute. [0, 59] + int tm_min; // Minutes after the hour. [0, 59] + int tm_hour; // Hours since midnight. [0, 23] + int tm_mday; // Day of the month. [1, 31] + int tm_mon; // Months since January. [0, 11] + int tm_year; // Years since 1900. (so, 2022 would be represented as 122) + int tm_wday; // Days since Sunday. [0, 6]. Sunday = 0. + int tm_yday; // Days since January 1st. [0, 365] (Day 365 = December 31st, on a leap year) + int tm_isdst; // Daylight savings time flag. + long int tm_gmtoff; // GNU extension: The offset from GMT in seconds. + const char *tm_zone; // GNU extension: The name of the time zone. +}; + +typedef struct +{ + int seconds, + minutes, + hours, + weekday, + day, + month, + year, + statusA, + statusB; +} +TimeStruct; + +TimeStruct *GetTime (); + +// Get the time (in milliseconds) since the OS has been started. +int GetTickCount(); + +int GetEpochTime(); +int GetEpochTimeFromTimeStruct(TimeStruct* ts); +void GetHumanTimeFromEpoch(int utime, TimeStruct* pOut); + +// Time manipulation functions. +/* +TODO + +double difftime(time_t a, time_t b); +time_t mktime (struct tm* ptr); +time_t time (time_t* timer); +int timespec_get(struct timespec* ptr, int base); +struct tm* gmtime_r(const time_t* timer, struct tm* result); +struct tm* gmtime (const time_t* timer); +*/ + +#endif//_TIME_TYPES_H diff --git a/crt2/include/nanoshell/types.h b/crt2/include/nanoshell/types.h new file mode 100644 index 00000000..9fc41e9b --- /dev/null +++ b/crt2/include/nanoshell/types.h @@ -0,0 +1,740 @@ +// nanoshell/types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _NANOSHELL_TYPES__H +#define _NANOSHELL_TYPES__H + +// Basic includes everyone should have +#include +#include +#include +#include +#include + +// Include other type files. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define false 0 +#define true 1 + +#define ARRAY_COUNT(array) (sizeof(array)/sizeof(*array)) + +#define UNUSED __attribute__((unused)) + +// Defines +#define WIN_KB_BUF_SIZE 512 + +//#define TITLE_BAR_HEIGHT 18 + +#define BACKGROUND_COLOR (GetThemingParameter(P_BACKGROUND_COLOR )) +#define BUTTON_MIDDLE_COLOR (GetThemingParameter(P_BUTTON_MIDDLE_COLOR )) +#define WINDOW_BACKGD_COLOR (GetThemingParameter(P_WINDOW_BACKGD_COLOR )) +#define WINDOW_TITLE_ACTIVE_COLOR (GetThemingParameter(P_WINDOW_TITLE_ACTIVE_COLOR )) +#define WINDOW_TITLE_INACTIVE_COLOR (GetThemingParameter(P_WINDOW_TITLE_INACTIVE_COLOR )) +#define WINDOW_TITLE_ACTIVE_COLOR_B (GetThemingParameter(P_WINDOW_TITLE_ACTIVE_COLOR_B )) +#define WINDOW_TITLE_INACTIVE_COLOR_B (GetThemingParameter(P_WINDOW_TITLE_INACTIVE_COLOR_B )) +#define WINDOW_TITLE_TEXT_COLOR_SHADOW (GetThemingParameter(P_WINDOW_TITLE_TEXT_COLOR_SHADOW )) +#define WINDOW_TITLE_TEXT_COLOR (GetThemingParameter(P_WINDOW_TITLE_TEXT_COLOR )) +#define WINDOW_TEXT_COLOR (GetThemingParameter(P_WINDOW_TEXT_COLOR )) +#define WINDOW_TEXT_COLOR_LIGHT (GetThemingParameter(P_WINDOW_TEXT_COLOR_LIGHT )) +#define SYSTEM_FONT ((int)GetThemingParameter(P_SYSTEM_FONT )) +#define TITLE_BAR_HEIGHT ((int)GetThemingParameter(P_TITLE_BAR_HEIGHT )) +#define TITLE_BAR_FONT ((int)GetThemingParameter(P_TITLE_BAR_FONT )) +#define SELECTED_MENU_ITEM_COLOR (GetThemingParameter(P_SELECTED_MENU_ITEM_COLOR )) +#define SELECTED_MENU_ITEM_COLOR_B (GetThemingParameter(P_SELECTED_MENU_ITEM_COLOR_B )) +#define WINDOW_BORDER_COLOR (GetThemingParameter(P_WINDOW_BORDER_COLOR )) +#define MENU_BAR_HEIGHT ((int)GetThemingParameter(P_MENU_BAR_HEIGHT )) +#define MENU_ITEM_HEIGHT ((int)GetThemingParameter(P_MENU_ITEM_HEIGHT )) +#define BORDER_SIZE ((int)GetThemingParameter(P_BORDER_SIZE )) +#define BUTTON_HILITE_COLOR (GetThemingParameter(P_BUTTON_HILITE_COLOR )) +#define BUTTON_SHADOW_COLOR (GetThemingParameter(P_BUTTON_SHADOW_COLOR )) +#define BUTTON_EDGE_COLOR (GetThemingParameter(P_BUTTON_EDGE_COLOR )) +#define BUTTON_HOVER_COLOR (GetThemingParameter(P_BUTTON_HOVER_COLOR )) +#define SCROLL_BAR_SIZE ((int)GetThemingParameter(P_SCROLL_BAR_SIZE )) +#define MENU_BAR_SELECTION_COLOR (GetThemingParameter(P_MENU_BAR_SELECTION_COLOR )) +#define SELECTED_ITEM_COLOR (GetThemingParameter(P_SELECTED_ITEM_COLOR )) +#define SELECTED_TEXT_COLOR (GetThemingParameter(P_SELECTED_TEXT_COLOR )) +#define DESELECTED_TEXT_COLOR (GetThemingParameter(P_DESELECTED_TEXT_COLOR )) +#define LIST_BACKGD_COLOR (GetThemingParameter(P_LIST_BACKGD_COLOR )) +#define TOOLTIP_BACKGD_COLOR (GetThemingParameter(P_TOOLTIP_BACKGD_COLOR )) +#define TOOLTIP_TEXT_COLOR (GetThemingParameter(P_TOOLTIP_TEXT_COLOR )) +#define SCROLL_BAR_BACKGD_COLOR (GetThemingParameter(P_SCROLL_BAR_BACKGD_COLOR )) +#define SELECTED_MENU_ITEM_TEXT_COLOR (GetThemingParameter(P_SELECTED_MENU_ITEM_TEXT_COLOR )) +#define DESELECTED_MENU_ITEM_TEXT_COLOR (GetThemingParameter(P_DESELECTED_MENU_ITEM_TEXT_COLOR )) +#define TABLE_VIEW_ALT_ROW_COLOR (GetThemingParameter(P_TABLE_VIEW_ALT_ROW_COLOR )) +#define BUTTON_XSHADOW_COLOR (GetThemingParameter(P_BUTTON_XSHADOW_COLOR )) +#define CAPTION_BUTTON_ICON_COLOR (GetThemingParameter(P_CAPTION_BUTTON_ICON_COLOR )) + +// Mark your system callbacks with this anyway!!! +#define CALLBACK + +#define MAKE_MOUSE_PARM(x, y) ((x)<<16|(y)) +#define GET_X_PARM(parm1) (parm1>>16) +#define GET_Y_PARM(parm1) (parm1&0xFFFF) + +#define TEXTEDIT_MULTILINE (1) +#define TEXTEDIT_LINENUMS (2) +#define TEXTEDIT_READONLY (4) +#define TEXTEDIT_STYLING (8) +#define TEXTEDIT_SYNTHILT (16) + +#define IMAGECTL_PAN (1) +#define IMAGECTL_ZOOM (2) +#define IMAGECTL_PEN (4) +#define IMAGECTL_FILL (8) + +// By default, the control's anchoring mode is: +// ANCHOR_LEFT_TO_LEFT | ANCHOR_RIGHT_TO_LEFT | ANCHOR_TOP_TO_TOP | ANCHOR_BOTTOM_TO_TOP + +// If the control's left edge anchors to the window's right edge. +// If this bit isn't set, the control's left edge anchors to the window's left edge. +#define ANCHOR_LEFT_TO_RIGHT 1 +// If the control's right edge anchors to the window's right edge. +// If this bit isn't set, the control's right edge anchors to the window's left edge. +#define ANCHOR_RIGHT_TO_RIGHT 2 +// If the control's top edge anchors to the window's bottom edge. +// If this bit isn't set, the control's top edge anchors to the window's top edge. +#define ANCHOR_TOP_TO_BOTTOM 4 +// If the control's bottom edge anchors to the window's bottom edge. +// If this bit isn't set, the control's bottom edge anchors to the window's top edge. +#define ANCHOR_BOTTOM_TO_BOTTOM 8 + +#define WF_NOCLOSE 0x00000001//Disable close button +#define WF_FROZEN 0x00000002//Freeze window +#define WF_NOTITLE 0x00000004//Disable title +#define WF_NOBORDER 0x00000008//Disable border +#define WF_NOMINIMZ 0x00000010//Disable minimize button +#define WF_ALWRESIZ 0x00000020//Allow resize +#define WF_NOMAXIMZ 0x00000080//Disable maximize button +#define WF_FLATBORD 0x00000100//Use a flat border instead of the regular border +#define WF_NOWAITWM 0x00000200//Prevent waiting for the window manager to update. Useful for games (1) +#define WF_BACKGRND 0x00000400//The window is on a separate 'background' layer, behind normal windows. +#define WF_FOREGRND 0x00000800//The window is on a separate 'foreground' layer, in front of normal windows. +#define WF_SYSPOPUP 0x10000000//System Popup (omit from taskbar) + +#define WINDOWS_MAX 256 +#define WINDOW_TITLE_MAX 250 +#define EVENT_QUEUE_MAX 256 + +#define KB_BUF_SIZE 512 + +#define LIST_ITEM_HEIGHT 16 +#define ICON_ITEM_WIDTH 90 +#define ICON_ITEM_HEIGHT 60 + +#define MB_OK 0x00000000 //The message box contains one push button: OK. This is the default. +#define MB_OKCANCEL 0x00000001 //The message box contains two push buttons: OK and Cancel. +#define MB_ABORTRETRYIGNORE 0x00000002 //The message box contains three push buttons: Abort, Retry and Ignore. +#define MB_YESNOCANCEL 0x00000003 //The message box contains three push buttons: Yes, No, and Cancel. +#define MB_YESNO 0x00000004 //The message box contains two push buttons: Yes, and No. +#define MB_RETRYCANCEL 0x00000005 //The message box contains two push buttons: Retry and Cancel. +#define MB_CANCELTRYCONTINUE 0x00000006 //The message box contains three push buttons: Cancel, Retry, and Continue. +#define MB_RESTART 0x00000007 //The message box contains one push button: Restart. + +// This flag tells the operating system that it may choose where to place a window. +// If the xPos and yPos are bigger than or equal to zero, the application tells the OS where it should place the window. +// The OS will use this as a guideline, for example, if an application wants to go off the screen, the OS +// will reposition its window to be fully inside the screen boundaries. +#define CW_AUTOPOSITION (-1) + +#define WINDOW_MIN_WIDTH (32) //that's already very small. +#define WINDOW_MIN_HEIGHT (3+TITLE_BAR_HEIGHT) + +#define WINDOW_MINIMIZED_WIDTH (160) +#define WINDOW_MINIMIZED_HEIGHT (3+TITLE_BAR_HEIGHT) + +#define TEXTSTYLE_HCENTERED (1) +#define TEXTSTYLE_VCENTERED (2) +#define TEXTSTYLE_WORDWRAPPED (4) +#define TEXTSTYLE_RJUSTIFY (8) +#define TEXTSTYLE_FORCEBGCOL (16)//VidDrawText does nothing with this flag, it's used for CONTROL_TEXTCENTER. +#define TEXTSTYLE_DJUSTIFY (32) + +#define GetImage(res) ((Image*)((res)->m_data)) +#define GetString(res) ((const char*)((res)->m_data)) + +#define DRE_RAISEDINNER (1 << 0) +#define DRE_SUNKENINNER (1 << 1) +#define DRE_RAISEDOUTER (1 << 2) +#define DRE_SUNKENOUTER (1 << 3) +#define DRE_BLACKOUTER (1 << 4) // takes priority over all these + +#define DRE_OUTER (DRE_RAISEDOUTER | DRE_SUNKENOUTER) +#define DRE_INNER (DRE_RAISEDINNER | DRE_SUNKENINNER) + +#define DRE_RAISED (DRE_RAISEDINNER | DRE_RAISEDOUTER) +#define DRE_SUNKEN (DRE_SUNKENINNER | DRE_SUNKENOUTER) + +#define DRE_FILLED (1 << 24) // 'bg' is ignored if this is not set +#define DRE_FLAT (1 << 25) // flat border. +#define DRE_HOT (1 << 26) // the button is hovered + +#define DRE_LEFT (1 << 27) +#define DRE_TOP (1 << 28) +#define DRE_RIGHT (1 << 29) +#define DRE_BOTTOM (1 << 30) +#define DRE_RECT (15 << 27) + +#define DRA_IGNOREXSIZE (1 << 0) // Ignores the width of the rectangle. +#define DRA_IGNOREYSIZE (1 << 1) // Ignores the height of the rectangle. + +#define DRA_CENTERX (1 << 2) // Center along the X axis. +#define DRA_CENTERY (1 << 3) // Center along the Y axis. + +// DRA_IGNOREXSIZE | DRA_IGNOREYSIZE means that both sizes are ignored, so the default size of ARROW_SIZE will be used. +#define DRA_IGNORESIZE (DRA_IGNOREXSIZE | DRA_IGNOREYSIZE) +#define DRA_CENTERALL (DRA_CENTERX | DRA_CENTERY) + +// Structs and enums + +#define DefaultConsoleColor 0x0F + +enum ConsoleType +{ + CONSOLE_TYPE_NONE, // uninitialized + CONSOLE_TYPE_TEXT, // always full screen + CONSOLE_TYPE_FRAMEBUFFER, // can either be the entire screen or just a portion of it. TODO + CONSOLE_TYPE_SERIAL, // just plain old serial + CONSOLE_TYPE_E9HACK, // Port E9 hack - qemu and bochs support this. + CONSOLE_TYPE_WINDOW, +}; + +enum +{ + EVENT_NULL, + EVENT_CREATE, // Shall be only called once, when a window or widget is created. + EVENT_DESTROY, // Shall be only called once, when a window or widget is destroyed. + EVENT_PAINT, + EVENT_MOVE, + EVENT_SIZE, + EVENT_ACTIVATE, + EVENT_SETFOCUS, + EVENT_KILLFOCUS, + EVENT_UPDATE, + EVENT_MOVECURSOR, + EVENT_CLICKCURSOR, + EVENT_RELEASECURSOR, + EVENT_COMMAND, + EVENT_KEYPRESS, + EVENT_CLOSE, + EVENT_KEYRAW, + EVENT_MINIMIZE,//do not call this normally. + EVENT_UNMINIMIZE, + EVENT_UPDATE2, + EVENT_MENU_CLOSE, + EVENT_CLICK_CHAR, + EVENT_MAXIMIZE, + EVENT_UNMAXIMIZE, + EVENT_IMAGE_REFRESH, + EVENT_RIGHTCLICK, + EVENT_RIGHTCLICKRELEASE, + EVENT_CHECKBOX, + EVENT_SCROLLDONE, + EVENT_MAX, + + EVENT_USER = 0x1000, +}; + +enum // eResourceType +{ + RES_NONE, + RES_STRING, + RES_ICON, + RES_BITMAP, + RES_BLOB, +}; +typedef int eResourceType; + +// Text Edit commands. TextInputRequestCommand +enum +{ + // clipboard commands. `parm` is ignored. + TEDC_PASTE, + TEDC_CUT, + TEDC_COPY, + + TEDC_INSERT, // inserts an arbitrary piece of text. Requires a parameter in `parm`. + + TEDC_GOTOLINE, // `parm` is here treated as a pointer to an integer + TEDC_GOTOOFFSET, // same here + TEDC_UNDO, + TEDC_SELECT_ALL, + TEDC_DELETE, +}; + +//NOTE WHEN WORKING WITH CONTROLS: +//While yes, the window manager technically supports negative comboIDs, you're not supposed +//to use them. They are used internally by other controls (for example list views and text input views). + +enum +{ + //A null control. Does nothing. + CONTROL_NONE, + //A text control printing text in its top-left corner. + CONTROL_TEXT, + //A control displaying an icon in the center of the rectangle. + CONTROL_ICON, + //A clickable button which triggers an EVENT_COMMAND with its comboID + //as its first parm. + CONTROL_BUTTON, + //A text input field. Not Finished + CONTROL_TEXTINPUT, + //A checkbox. Not Finished. + CONTROL_CHECKBOX, + //A clickable label, which renders its text in the center-left. + //Does the same as the CONTROL_BUTTON. + CONTROL_CLICKLABEL, + //A text control printing text in the center of the rectangle. + CONTROL_TEXTCENTER, + //A clickable button which triggers an event based on this->m_parm1 + //with its comboID as its first parm. + CONTROL_BUTTON_EVENT, + //A list view. Complicated. + CONTROL_LISTVIEW, + //A vertical scroll bar. + CONTROL_VSCROLLBAR, + //A horizontal scroll bar. + CONTROL_HSCROLLBAR, + //A menu bar attached to the top of a window. + //Adding more than one control is considered UB + CONTROL_MENUBAR, + //A text control printing big text (>127 chars) + CONTROL_TEXTHUGE, + //Same as CONTROL_LISTVIEW but with bigger icons. + CONTROL_ICONVIEW, + //Does nothing except surround other controls with a rectangle. Useful for grouping settings. + CONTROL_SURROUND_RECT, + //Button with a colored background (parm2) + CONTROL_BUTTON_COLORED, + //Button as part of a list + CONTROL_BUTTON_LIST, + //Button with an icon on top. Parm1= icon type, Parm2= icon size (16 or 32) + CONTROL_BUTTON_ICON, + //Button with an icon on top. Parm1= icon type, Parm2= icon size (16 or 32) + CONTROL_BUTTON_ICON_BAR, + //A simple line control + CONTROL_SIMPLE_HLINE, + //Image control. A _valid_ pointer to an Image structure must be passed into parm1. + //When creating the control, the image gets duplicated, so the caller may free/dispose of + //the old image. The system will get rid of its own copy when the control gets destroyed. + CONTROL_IMAGE, + //Task list control + CONTROL_TASKLIST, + //Same as CONTROL_ICONVIEW but with draggable icons. + CONTROL_ICONVIEWDRAG, + //A table view. Even more complicated. + CONTROL_TABLEVIEW, + //Checkable icon button. + CONTROL_BUTTON_ICON_CHECKABLE, + //This control is purely to identify how many controls we support + //currently. This control is unsupported and will crash your application + //if you use this. + CONTROL_COUNT, + + CONTROL_SIMPLE_VLINE = -CONTROL_SIMPLE_HLINE, // macro for CONTROL_SIMPLE_HLINE with parm1 = 1 +}; + +enum +{ + MBID_OK = 0x10010, + MBID_CANCEL, + MBID_ABORT, + MBID_RETRY, + MBID_IGNORE, + MBID_YES, + MBID_NO, + MBID_TRY_AGAIN, + MBID_CONTINUE, + MBID_COUNT, +}; + +enum { + P_BLACK, + P_BACKGROUND_COLOR, + P_BUTTON_MIDDLE_COLOR, + P_WINDOW_BACKGD_COLOR, + P_WINDOW_BORDER_COLOR, + P_WINDOW_TITLE_ACTIVE_COLOR, + P_WINDOW_TITLE_INACTIVE_COLOR, + P_WINDOW_TITLE_ACTIVE_COLOR_B, + P_WINDOW_TITLE_INACTIVE_COLOR_B, + P_WINDOW_TITLE_TEXT_COLOR_SHADOW, + P_WINDOW_TITLE_TEXT_COLOR, + P_WINDOW_TEXT_COLOR, + P_WINDOW_TEXT_COLOR_LIGHT, + P_SYSTEM_FONT, + P_TITLE_BAR_HEIGHT, + P_TITLE_BAR_FONT, + P_SELECTED_MENU_ITEM_COLOR, + P_SELECTED_MENU_ITEM_COLOR_B, + P_MENU_BAR_HEIGHT, + P_BORDER_SIZE, + P_BUTTON_HILITE_COLOR, + P_BUTTON_SHADOW_COLOR, + P_BUTTON_EDGE_COLOR, + P_BUTTON_HOVER_COLOR, + P_SCROLL_BAR_SIZE, + P_MENU_BAR_SELECTION_COLOR, + P_SELECTED_ITEM_COLOR, + P_SELECTED_TEXT_COLOR, + P_DESELECTED_TEXT_COLOR, + P_LIST_BACKGD_COLOR, + P_TOOLTIP_BACKGD_COLOR, + P_TOOLTIP_TEXT_COLOR, + P_SCROLL_BAR_BACKGD_COLOR, + P_SELECTED_MENU_ITEM_TEXT_COLOR, + P_DESELECTED_MENU_ITEM_TEXT_COLOR, + P_TABLE_VIEW_ALT_ROW_COLOR, + P_MENU_ITEM_HEIGHT, + P_BUTTON_XSHADOW_COLOR, + P_CAPTION_BUTTON_ICON_COLOR, +}; + +enum +{ + /*0x80*/TIST_BOLD = '\x80', + /*0x81*/TIST_UNDERLINE, + /*0x82*/TIST_ITALIC, + /*0x83*/TIST_RED, + /*0x84*/TIST_BLUE, + /*0x85*/TIST_GREEN, + /*0x86*/TIST_LINK, + /*0x87*/TIST_UNFORMAT, + /*0x88*/TIST_UNBOLD, + /*0x89*/TIST_UNITALIC, + /*0x8A*/TIST_UNUNDERLINE, + /*0x8B*/TIST_UNCOLOR, + /*0x8C*/TIST_UNLINK, + /*0x8D*/TIST_COUNT, +}; + +enum +{ + CLIPBOARD_DATA_NONE, + CLIPBOARD_DATA_INTEGER, + CLIPBOARD_DATA_BINARY, + CLIPBOARD_DATA_TEXT, + CLIPBOARD_DATA_LARGE_TEXT, + + // Add more clipboard data types here. Unknown clipboard data types will be treated as generic binaries +}; + +typedef enum eArrowType +{ + DRA_UP, + DRA_DOWN, + DRA_LEFT, + DRA_RIGHT, +} +eArrowType; + +//Console +typedef struct ConsoleStruct +{ + int type; // ConsoleType enum + int width, height; // width and height + uint16_t *textBuffer; // unused in fb mode + uint16_t color; // colors + int curX, curY; // cursor X and Y positions + bool pushOrWrap;// check if we should push whole screen up, or clear&wrap + VBEData* m_vbeData;//vbe data to switch to when drawing, ONLY APPLIES TO CONSOLE_TYPE_WINDOW!! + int offX, offY; + int font; + int cwidth, cheight; + bool m_dirty; + char m_inputBuffer[KB_BUF_SIZE]; + int m_inputBufferBeg, m_inputBufferEnd; + int m_cursorFlashTimer, m_cursorFlashState; +} +Console; + + +struct WindowStruct; +struct ControlStruct; +typedef bool (*WidgetEventHandler) (struct ControlStruct*, int eventType, int parm1, int parm2, struct WindowStruct* parentWindow); +typedef void (*WindowProc) (struct WindowStruct*, int, int, int); + +typedef struct +{ + int m_icon;//can be blank + char m_contents [128]; +} +ListItem; + +typedef struct +{ + bool m_hasIcons; + int m_elementCount, m_capacity; + int m_scrollY; + int m_highlightedElementIdx; + ListItem *m_pItems; +} +ListViewData; + +typedef struct +{ + bool m_isBeingDragged, m_clickedBefore; + bool m_yMinButton, m_yMaxButton; + int m_min, m_max, m_pos, m_dbi; +} +ScrollBarData; + +typedef struct tagMenuBarTreeItem +{ + int m_comboID;//can be searchable + int m_childrenCount, + m_childrenCapacity;//if childrenCount reaches this and we need to add another, double this + struct tagMenuBarTreeItem* m_childrenArray; + char m_text [104]; + //if this value is set, it gets drawn if this is an item part of the root tree, or the parent is open too. + bool m_isOpen; +} +MenuBarTreeItem; + +typedef struct +{ + bool m_clicked, + m_hovered; +} +ButtonData; + +typedef struct +{ + MenuBarTreeItem m_root; +} +MenuBarData; + +typedef struct +{ + Image *pImage; + uint32_t nCurrentColor; + //if parm2 flag IMAGECTL_PAN is set, this has an effect. By default it is 0 + int nCurPosX, nCurPosY; + //to track cursor movement delta + int nLastXGot, nLastYGot; + //if parm2 flag IMAGECTL_ZOOM is set, this has an effect on the resulting image. By default it is the same as nWidth. + //to get height, you do (int)(((long long)nHeight * nZoomedWidth) / nWidth) + int nZoomedWidth; +} +ImageCtlData; + +typedef struct +{ + bool m_focused; + bool m_dirty;//Has it been changed since the dirty flag was set to false? + bool m_onlyOneLine, m_showLineNumbers;//note that these are mutually exclusive, but both can be turned off + int m_textCapacity, m_textLength;//The text length needs to be 1 less than the text capacity. + //If the text capacity is 65, for example, the textLength may not be bigger than 64. + int m_textCursorIndex, m_textCursorSelStart, m_textCursorSelEnd, + m_scrollY; + char* m_pText; + bool m_readOnly; + bool m_enableStyling; +} +TextInputData; + +typedef struct +{ + bool m_clicked; + bool m_checked; +} +CheckBoxData; + +typedef struct ControlStruct +{ + bool m_active; + int m_type;//CONTROL_XXX + int m_parm1, m_parm2; + int m_comboID; + char m_text[128]; + void* m_dataPtr; + Rectangle m_rect; + bool m_bMarkedForDeletion; + bool m_bFocused; + bool m_bVisible; + + //data for controls: + union + { + ListViewData m_listViewData; + ScrollBarData m_scrollBarData; + ButtonData m_buttonData; + MenuBarData m_menuBarData; + TextInputData m_textInputData; + CheckBoxData m_checkBoxData; + ImageCtlData m_imageCtlData; + }; + + int m_anchorMode; + + //event handler + WidgetEventHandler OnEvent; + + // A rect that was tried. This is what the control's size _should_ be, + // but due to some limitation m_triedRect may not match m_rect. + // The smallest rectangle a control can occupy is 10x10. + Rectangle m_triedRect; + + bool m_bDisabled; +} +Control; + +// DON'T rely on this!!! This is an internal kernel struct and can be changed. + +typedef struct WindowStruct +{ + bool m_used; + bool m_minimized; + bool m_hidden; + bool m_isBeingDragged; + bool m_isSelected; + + bool m_renderFinished; + + char* m_title; + + int m_flags; + + WindowProc m_callback; + Rectangle m_rect; + Rectangle m_rectBackup; + VBEData m_vbeData; + + int m_iconID; + + bool m_eventQueueLockUnused; // left to keep compatibity with old ELFs that modify the window structure directly + short* m_eventQueue; + int* m_eventQueueParm1; + int* m_eventQueueParm2; + //short m_eventQueue[EVENT_QUEUE_MAX]; + //int m_eventQueueParm1[EVENT_QUEUE_MAX]; + //int m_eventQueueParm2[EVENT_QUEUE_MAX]; + int m_eventQueueSize; + + int m_minWidth, m_minHeight; + + bool m_markedForDeletion; + + Control* m_pControlArray; + int m_controlArrayLen; + + void* m_data; //user data + + void *m_pOwnerThread, + *m_pSubThread;//in case you ever want to use this + + Console* m_consoleToFocusKeyInputsTo; + + bool m_bWindowManagerUpdated; + + int m_cursorID; + + bool m_maximized; + + // Raw input buffer. + char* m_inputBuffer; + int m_inputBufferBeg, m_inputBufferEnd; + + bool m_clickedInside; + + SafeLock m_EventQueueLock; + + Rectangle m_taskbarRect; + + Cursor m_customCursor; + + int m_frequentWindowRenders; + int m_lastSentPaintEventExternallyWhen; + + int m_cursorID_backup; + + int m_lastHandledMessagesWhen; + + //these two booleans are updated by UpdateDepthBuffer() internally + //if none of the window's pixels are visible + bool m_bObscured; + //if all of the window's pixels are visible at the same time + //(we can optimize drawing by just VidBitBlitting it directly + //to the screen, instead of taking occlusion into account) + bool m_bForemost; + + //avoid a data race while resizing the screen + SafeLock m_screenLock; +} Window; + +typedef Window* PWINDOW; + + +//BetterStrTok: https://github.com/iProgramMC/BetterStrTok +typedef struct { + bool m_bInitted; + char*m_pContinuation; + char*m_pReturnValue; +} TokenState; + +typedef struct +{ + SafeLock m_lock; + + int m_type; + char m_short_str[256]; + int m_blob_size; + + union { + int m_int_data; + void *m_generic_data_ptr; + char *m_char_str; + }; +} +ClipboardVariant; + +typedef struct +{ + // The resource's ID. + int m_id; + + // The type of resource. + eResourceType m_type; + + // The size of the binary data following this resource. + int m_size; + + // The data after the resource. + char m_data[]; +} +Resource; + +typedef struct MenuEntry +{ + Window* pWindow; + struct MenuEntry* pMenuEntries; + int nMenuEntries; + int nMenuComboID; + int nOrigCtlComboID; + char sText[100]; + bool bOpen; + bool bHasIcons; + int nLineSeparators; + Window* pOpenWindow; + int nIconID; + int nWidth; // The width of the menu window (if this has children) + int nHeight; // The height of the menu window + int nItemWidth; // The width of this item when the parent menu is opened. If 0, this fills to the width. + int nItemX; // The X position of the item. + int nItemY; // The Y position of the item. If 0, this is automatically laid out. + bool bDisabled; + bool bPrivate; // uses a private event rather than a public one. +} +WindowMenu; + +#endif//_NANOSHELL_TYPES__H \ No newline at end of file diff --git a/crt2/include/nanoshell/unistd_types.h b/crt2/include/nanoshell/unistd_types.h new file mode 100644 index 00000000..a957e330 --- /dev/null +++ b/crt2/include/nanoshell/unistd_types.h @@ -0,0 +1,72 @@ +// nanoshell/unistd_types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _NANOSHELL_UNISTD_TYPES__H +#define _NANOSHELL_UNISTD_TYPES__H + +#include + +#define PATH_MAX (260) +#define PATH_SEP ('/') +#define PATH_THISDIR (".") +#define PATH_PARENTDIR ("..") + +#define PERM_READ (1) +#define PERM_WRITE (2) +#define PERM_EXEC (4) + +#define O_RDONLY (1) +#define O_WRONLY (2) +#define O_RDWR (O_RDONLY | O_WRONLY) +#define O_APPEND (4) +#define O_CREAT (8) +#define O_NONBLOCK (16) +#define O_TRUNC (32) +#define O_EXEC (1024) + +//lseek whences +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +typedef struct +{ + uint32_t m_type; + uint32_t m_size; + uint32_t m_blocks; + uint32_t m_inode; + + uint32_t m_perms; + uint32_t m_modifyTime; + uint32_t m_createTime; +} +StatResult; + +enum +{ + FILE_TYPE_NONE = 0, + FILE_TYPE_FILE, + FILE_TYPE_CHAR_DEVICE, + FILE_TYPE_BLOCK_DEVICE, + FILE_TYPE_PIPE, + FILE_TYPE_SYMBOLIC_LINK, + FILE_TYPE_DIRECTORY = 8, + FILE_TYPE_MOUNTPOINT = 16, //to be OR'd into the other flags +}; + +enum +{ + IOCTL_NO_OP, // This can be used to test if the device actually supports I/O control. Does nothing. + + // Define the starting places of I/O controls for each device. + IOCTL_TERMINAL_START = 10000, + IOCTL_SOUNDDEV_START = 20000, + //... + + IOCTL_TERMINAL_GET_SIZE = IOCTL_TERMINAL_START, // argp points to a Point structure, which will get filled in. + IOCTL_TERMINAL_SET_ECHO_INPUT, // enable or disable echoing input in CoGetString() + + IOCTL_SOUNDDEV_SET_SAMPLE_RATE = IOCTL_SOUNDDEV_START, // Set the sample rate of an audio playback device. +}; + +#endif//_NANOSHELL_UNISTD_TYPES__H \ No newline at end of file diff --git a/crt2/include/nsstandard.h b/crt2/include/nsstandard.h new file mode 100644 index 00000000..27f900f9 --- /dev/null +++ b/crt2/include/nsstandard.h @@ -0,0 +1,4 @@ +// nsstandard.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#include diff --git a/crt2/include/nsstructs.h b/crt2/include/nsstructs.h new file mode 100644 index 00000000..bb410bfe --- /dev/null +++ b/crt2/include/nsstructs.h @@ -0,0 +1,4 @@ +// nsstructs.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#include diff --git a/crt2/include/setjmp.h b/crt2/include/setjmp.h new file mode 100644 index 00000000..28439a54 --- /dev/null +++ b/crt2/include/setjmp.h @@ -0,0 +1,16 @@ +// setjmp.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _SETJMP___H +#define _SETJMP___H + +#include + +__attribute__((returns_twice)) int setjmp (jmp_buf buffer); +__attribute__((noreturn)) void longjmp(jmp_buf buffer, int value); + +// The NanoShell names for the functions (they're the same) +__attribute__((returns_twice)) int SetJump (JumpBuffer env); +__attribute__((noreturn)) void LongJump(JumpBuffer env, int value); + +#endif//_SETJMP___H \ No newline at end of file diff --git a/crt2/include/stdalign.h b/crt2/include/stdalign.h new file mode 100644 index 00000000..b2e53a44 --- /dev/null +++ b/crt2/include/stdalign.h @@ -0,0 +1,10 @@ +#ifndef _STDALIGN_H +#define _STDALIGN_H 1 + +#define alignas _Alignas +#define alignof _Alignof + +#define __alignas_is_defined 1 +#define __alignof_is_defined 1 + +#endif diff --git a/crt2/include/stdarg.h b/crt2/include/stdarg.h new file mode 100644 index 00000000..fa243e0b --- /dev/null +++ b/crt2/include/stdarg.h @@ -0,0 +1,11 @@ +#ifndef _STDARG_H +#define _STDARG_H 1 + +typedef __builtin_va_list va_list; + +#define va_start(v, l) __builtin_va_start(v, l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v, l) __builtin_va_arg(v, l) +#define va_copy(d, s) __builtin_va_copy(d, s) + +#endif diff --git a/crt2/include/stdbool.h b/crt2/include/stdbool.h new file mode 100644 index 00000000..bb2328c1 --- /dev/null +++ b/crt2/include/stdbool.h @@ -0,0 +1,11 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H 1 + +#define bool _Bool + +#define true 1 +#define false 0 + +#define __bool_true_false_are_defined 1 + +#endif diff --git a/crt2/include/stddef.h b/crt2/include/stddef.h new file mode 100644 index 00000000..4b85c130 --- /dev/null +++ b/crt2/include/stddef.h @@ -0,0 +1,16 @@ +#ifndef _STDDEF_H +#define _STDDEF_H 1 + +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __WCHAR_TYPE__ wchar_t; + +#ifdef NULL +#undef NULL +#endif + +#define NULL ((void *)0) + +#define offsetof(s, m) __builtin_offsetof(s, m) + +#endif diff --git a/crt2/include/stdint.h b/crt2/include/stdint.h new file mode 100644 index 00000000..5f977daf --- /dev/null +++ b/crt2/include/stdint.h @@ -0,0 +1,107 @@ +#ifndef _STDINT_H +#define _STDINT_H 1 + +typedef __UINT8_TYPE__ uint8_t; +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT32_TYPE__ uint32_t; +typedef __UINT64_TYPE__ uint64_t; + +typedef __UINT_LEAST8_TYPE__ uint_least8_t; +typedef __UINT_LEAST16_TYPE__ uint_least16_t; +typedef __UINT_LEAST32_TYPE__ uint_least32_t; +typedef __UINT_LEAST64_TYPE__ uint_least64_t; + +typedef __UINT_FAST8_TYPE__ uint_fast8_t; +typedef __UINT_FAST16_TYPE__ uint_fast16_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +typedef __UINT_FAST64_TYPE__ uint_fast64_t; + +typedef __INT8_TYPE__ int8_t; +typedef __INT16_TYPE__ int16_t; +typedef __INT32_TYPE__ int32_t; +typedef __INT64_TYPE__ int64_t; + +typedef __INT_LEAST8_TYPE__ int_least8_t; +typedef __INT_LEAST16_TYPE__ int_least16_t; +typedef __INT_LEAST32_TYPE__ int_least32_t; +typedef __INT_LEAST64_TYPE__ int_least64_t; + +typedef __INT_FAST8_TYPE__ int_fast8_t; +typedef __INT_FAST16_TYPE__ int_fast16_t; +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __INT_FAST64_TYPE__ int_fast64_t; + +typedef __UINTPTR_TYPE__ uintptr_t; +typedef __INTPTR_TYPE__ intptr_t; + +typedef __UINTMAX_TYPE__ uintmax_t; +typedef __INTMAX_TYPE__ intmax_t; + +#define UINT8_MAX __UINT8_MAX__ +#define UINT16_MAX __UINT16_MAX__ +#define UINT32_MAX __UINT32_MAX__ +#define UINT64_MAX __UINT64_MAX__ + +#define INT8_MAX __INT8_MAX__ +#define INT16_MAX __INT16_MAX__ +#define INT32_MAX __INT32_MAX__ +#define INT64_MAX __INT64_MAX__ + +#define INT8_MIN (-INT8_MAX - 1) +#define INT16_MIN (-INT16_MAX - 1) +#define INT32_MIN (-INT32_MAX - 1) +#define INT64_MIN (-INT64_MAX - 1) + +#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__ +#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__ +#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__ +#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__ + +#define INT_LEAST8_MAX __INT_LEAST8_MAX__ +#define INT_LEAST16_MAX __INT_LEAST16_MAX__ +#define INT_LEAST32_MAX __INT_LEAST32_MAX__ +#define INT_LEAST64_MAX __INT_LEAST64_MAX__ + +#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1) +#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1) +#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1) +#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1) + +#define UINT_FAST8_MAX __UINT_FAST8_MAX__ +#define UINT_FAST16_MAX __UINT_FAST16_MAX__ +#define UINT_FAST32_MAX __UINT_FAST32_MAX__ +#define UINT_FAST64_MAX __UINT_FAST64_MAX__ + +#define INT_FAST8_MAX __INT_FAST8_MAX__ +#define INT_FAST16_MAX __INT_FAST16_MAX__ +#define INT_FAST32_MAX __INT_FAST32_MAX__ +#define INT_FAST64_MAX __INT_FAST64_MAX__ + +#define INT_FAST8_MIN (-INT_FAST8_MAX - 1) +#define INT_FAST16_MIN (-INT_FAST16_MAX - 1) +#define INT_FAST32_MIN (-INT_FAST32_MAX - 1) +#define INT_FAST64_MIN (-INT_FAST64_MAX - 1) + +#define UINTPTR_MAX __UINTPTR_MAX__ +#define INTPTR_MAX __INTPTR_MAX__ +#define INTPTR_MIN (-INTPTR_MAX - 1) + +#define UINTMAX_MAX __UINTMAX_MAX__ +#define INTMAX_MAX __INTMAX_MAX__ +#define INTMAX_MIN (-INTMAX_MAX - 1) + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define PTRDIFF_MIN (-PTRDIFF_MAX - 1) + +#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__ +#define SIG_ATOMIC_MIN (-SIG_ATOMIC_MAX - 1) + +#define SIZE_MAX __SIZE_MAX__ + +#define WCHAR_MAX __WCHAR_MAX__ +#define WCHAR_MIN (-WCHAR_MAX - 1) + +#define WINT_MAX __WINT_MAX__ +#define WINT_MIN (-WINT_MAX - 1) + +#endif diff --git a/crt2/include/stdio.h b/crt2/include/stdio.h new file mode 100644 index 00000000..607f6f0c --- /dev/null +++ b/crt2/include/stdio.h @@ -0,0 +1,59 @@ +// stdio.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _STDIO__H +#define _STDIO__H + +#include + +// Standard C file access functions + +FILE* fopen (const char* file, const char* mode); +FILE* fdopen(int fd, const char* mode); +int fclose(FILE* file); +int fread ( void* ptr, size_t size, size_t nmemb, FILE* stream); +int fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream); +int fseek (FILE* file, int offset, int whence); +int ftell (FILE* file); +int fflush(FILE* file); +int fputs (const char* s, FILE* stream); +int fputc (int c, FILE* stream); +int putc (int c, FILE* stream); +int feof (FILE* file); +int getc (FILE* file); +void rewind(FILE* file); +int vfprintf (FILE* file, const char* fmt, va_list list); +int fprintf (FILE* file, const char* fmt, ...); +int vprintf (const char* fmt, va_list list); + +// Operations on files +int remove(const char* filename); +int rename(const char* oldname, const char* newname); +int renameat(int olddirdd, const char* oldname, int newdirdd, const char* newname); + +// External definitions to stdin, stdout and stderr +extern FILE *stdin, *stdout, *stderr; + +// Formatted input/output +int vfprintf(FILE* file, const char* fmt, va_list list); +int fprintf (FILE* file, const char* fmt, ...); +int vsnprintf(char* OutBuffer, size_t BufferSize, const char* FormatType, va_list list); +int vsprintf (char* OutBuffer, const char* FormatType, va_list list); +int snprintf (char* OutBuffer, size_t BufferSize, const char* fmt, ...); +int sprintf (char* OutBuffer, const char* FormatType, ...); +void LogMsg (const char* Format, ...); +void LogMsgNoCr(const char* Format, ...); +int printf (const char* Format, ...); + +// Character input/output +int fputs(const char* s, FILE * stream); +int fputc(int c, FILE * stream); +int putc(int c, FILE* stream); +int puts(const char * s); +int putchar(int c); +int fgetc(FILE* stream); +int getc(FILE* stream); +int ungetc(int c, FILE * stream); +int ferror(FILE* stream); + +#endif//_STDIO__H \ No newline at end of file diff --git a/crt2/include/stdlib.h b/crt2/include/stdlib.h new file mode 100644 index 00000000..aa122cd5 --- /dev/null +++ b/crt2/include/stdlib.h @@ -0,0 +1,63 @@ +// stdlib.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _STDLIB__H +#define _STDLIB__H + +#include +#include +#include + +// Numeric conversion functions +long long atoll (const char* str); +int atoi (const char* str); +int atox (const char* str); +long atol (const char* str); +double atof (const char *arr); +char* itoa (int value, char* buffer, int radix); +char* ltoa (long value, char* buffer, int radix); + +// Memory management +void* malloc (size_t size); +void* calloc (size_t nmemb, size_t size); +void free (void* ptr); +void* realloc(void* ptr, size_t sz); + +// Arithmetic utilities +int abs(int k); +double fabs(double x); + +// Random number generator +int GetRandom(void); // use the kernel RNG +int rand(void); +int rand_r(unsigned int * seed); +void srand(unsigned int seed); + +// Communication with the environment +__attribute__((noreturn)) void exit(int status); +__attribute__((noreturn)) void abort(void); +//TODO: atexit +//TODO: getenv +//TODO: system + +// Sorting functions +// NOTE: I'd prefer you don't use these right now. They're just bubble sort. I'll implement a smarter sorting algorithm later on. +void qsort(void *pBase, size_t nCount, size_t nElementSize, ComparisonFunc pCompare); +void qsort_r(void *pBase, size_t nCount, size_t nElementSize, ComparisonReentrantFunc pCompareReentrant, void* pArgument); + +// Environment +char* getenv(const char* name); + +// Safe string conversion +unsigned long long strtoux(const char* str, char ** endptr, int base, unsigned long long max); +long long strtox(const char* str, char ** endptr, int base, long long max); +unsigned long long int strtoull(const char* str, char ** endptr, int base); +unsigned long int strtoul(const char* str, char ** endptr, int base); +long long int strtoll(const char* str, char ** endptr, int base); +long int strtol(const char* str, char ** endptr, int base); +double ldexp(double val, int exp); +double strtod(const char* str, char** endptr); +long double strtold(const char* str, char** endptr); +float strtof(const char* str, char** endptr); + +#endif//_STDLIB__H diff --git a/crt2/include/stdnoreturn.h b/crt2/include/stdnoreturn.h new file mode 100644 index 00000000..9c2691f4 --- /dev/null +++ b/crt2/include/stdnoreturn.h @@ -0,0 +1,6 @@ +#ifndef _STDNORETURN_H +#define _STDNORETURN_H 1 + +#define noreturn _Noreturn + +#endif diff --git a/crt2/include/string.h b/crt2/include/string.h new file mode 100644 index 00000000..510c831f --- /dev/null +++ b/crt2/include/string.h @@ -0,0 +1,39 @@ +// string.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _STRING__H +#define _STRING__H + +#include + +int memcmp (const void* s1, const void* s2, size_t n); +void* memcpy (void* dest, const void* src, size_t n); +void* memmove (void* dest, const void* src, size_t n); +void* memset (void* s, int c, size_t n); +size_t strlen (const char* s); +char* strcpy (char *dest, const char* src); +char* strncpy (char *dest, const char *src, size_t n); +size_t strlcpy (char *dest, const char *src, size_t n); +int strcmp (const char* as, const char* bs); +int strncmp (const char* s1, const char* s2, size_t n); +char* strcat (char* dest, const char* after); +void strtolower (char* as); +void strtoupper (char* as); +void memtolower (char* as, int w); +void memtoupper (char* as, int w); +size_t strgetlento(const char* str, char chr); +char* strdup (const char *pText); +char* strstr (const char *haystack, const char *needle); +size_t strnlen (const char* str, size_t szmax); +char* strchr (const char* s, int c); +char* strrchr (const char* s, int c); +char* strchrnul (const char* s, int c); // GNU extension +size_t strspn (const char* s, const char* accept); +size_t strcspn (const char* s, const char* reject); +void* memchr (const void* s, int c, size_t n); +void* memrchr (const void* s, int c, size_t n); +void* rawmemchr (const void* s, int c); + +const char * strerror (int errnum); + +#endif//_STRING__H diff --git a/crt2/include/sys/mman.h b/crt2/include/sys/mman.h new file mode 100644 index 00000000..79084517 --- /dev/null +++ b/crt2/include/sys/mman.h @@ -0,0 +1,12 @@ +// sys/mman.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __SYS__MMAN_H +#define __SYS__MMAN_H + +#include + +void* mmap(void *, size_t, int, int, int, off_t); +int munmap(void *, size_t); + +#endif//__SYS__MMAN_H \ No newline at end of file diff --git a/crt2/include/sys/time.h b/crt2/include/sys/time.h new file mode 100644 index 00000000..647ffe63 --- /dev/null +++ b/crt2/include/sys/time.h @@ -0,0 +1,9 @@ +// sys/time.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __SYS_TIME_H +#define __SYS_TIME_H + +//TODO + +#endif//__SYS_TIME_H \ No newline at end of file diff --git a/crt2/include/sys/types.h b/crt2/include/sys/types.h new file mode 100644 index 00000000..c07dfe3d --- /dev/null +++ b/crt2/include/sys/types.h @@ -0,0 +1,25 @@ +// sys/types.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef __SYS__TYPES_H +#define __SYS__TYPES_H + +#include +#include +#include +#include + +typedef long mode_t; +typedef long nlink_t; +typedef long uid_t; +typedef long gid_t; +typedef long id_t; +typedef long pid_t; +typedef long tid_t; +typedef long blkcnt_t; +typedef long off_t; +typedef unsigned long fsblkcnt_t; +typedef unsigned long fsfilcnt_t; +typedef unsigned long ino_t; + +#endif//__SYS__TYPES_H \ No newline at end of file diff --git a/crt2/include/time.h b/crt2/include/time.h new file mode 100644 index 00000000..5385240f --- /dev/null +++ b/crt2/include/time.h @@ -0,0 +1,22 @@ +// time.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _TIME___H +#define _TIME___H + +#include + +double difftime(time_t a, time_t b); +time_t time(time_t* timer); +time_t mktime (struct tm * ptr); +struct tm* localtime_r(const time_t* timep, struct tm * result); +struct tm* localtime(const time_t* timep); + +// NanoShell specifics +void StructTmToTimeStruct(TimeStruct* out, struct tm* in); +void TimeStructToStructTm(TimeStruct* in, struct tm* out); +int GetEpochTimeFromTimeStruct(TimeStruct* ts); +int GetEpochTime(); +void GetHumanTimeFromEpoch(int utime, TimeStruct* pOut); + +#endif//_TIME___H diff --git a/crt2/include/unistd.h b/crt2/include/unistd.h new file mode 100644 index 00000000..7fb5082d --- /dev/null +++ b/crt2/include/unistd.h @@ -0,0 +1,18 @@ +// unistd.h +// Copyright (C) 2022 iProgramInCpp +// The NanoShell Standard C Library +#ifndef _UNISTD_H +#define _UNISTD_H + +#include + +// POSIX file access functions +int open (const char* path, int oflag); +int close (int fd); +int read (int fd, void* buf, unsigned int nbyte); +int write (int fd, const void* buf, unsigned int nbyte); +int lseek (int fd, int offset, int whence); +int tellf (int fd); +int tellsz(int fd); + +#endif//_UNISTD_H \ No newline at end of file diff --git a/crt2/lib_i386.ld b/crt2/lib_i386.ld new file mode 100644 index 00000000..f901b493 --- /dev/null +++ b/crt2/lib_i386.ld @@ -0,0 +1,3 @@ +/* Tell the linker that we want an ELF32 output file */ +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386) diff --git a/crt2/src/a_assert.c b/crt2/src/a_assert.c new file mode 100644 index 00000000..1a52480b --- /dev/null +++ b/crt2/src/a_assert.c @@ -0,0 +1,28 @@ +// *************************************************************** +// a_assert.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +__attribute__((noreturn)) +void abort() +{ + // TODO + *((uint32_t*)0xFFFFFFF4) = 0xFFFFFFFF; + while (true); +} + +void OnAssertionFail(const char *cond_msg, const char *file, int line) +{ + LogMsg("ASSERTION FAILED!"); + LogMsg("The assertion \"%s\" failed at %s:%d.", cond_msg, file, line); + LogMsg("The program will now exit."); + abort(); +} diff --git a/crt2/src/a_cc.c b/crt2/src/a_cc.c new file mode 100644 index 00000000..77ffffd3 --- /dev/null +++ b/crt2/src/a_cc.c @@ -0,0 +1,17 @@ +// *************************************************************** +// a_cc.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +int CcRunCCode(const char* data, int length) +{ + return _I_CcRunCCode (data, length); +} diff --git a/crt2/src/a_env.c b/crt2/src/a_env.c new file mode 100644 index 00000000..751084dd --- /dev/null +++ b/crt2/src/a_env.c @@ -0,0 +1,18 @@ +// *************************************************************** +// a_env.c - Creation date: 29/12/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +char* getenv(UNUSED const char* name) +{ + // TODO: Call into the system and grab info from ns.ini. + return NULL; +} diff --git a/crt2/src/a_error.c b/crt2/src/a_error.c new file mode 100644 index 00000000..4920bfcb --- /dev/null +++ b/crt2/src/a_error.c @@ -0,0 +1,57 @@ +// *************************************************************** +// a_error.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +// note: The error number is stored as positive. +int g_errorNum = 0; + +static __attribute__((always_inline)) +int absolute_value(int a) +{ + return a < 0 ? -a : a; +} + +int SetErrorNumber(int err) +{ + g_errorNum = absolute_value(err); + return -1; +} + +int* GetErrorNumberPointer() +{ + return &g_errorNum; +} + +int GetErrorNumber() +{ + return g_errorNum; +} + +const char* strerror(int errnum) +{ + return ErrNoStr(-errnum); +} + +int seterrno(int en) +{ + return g_errorNum = absolute_value(en); +} + +int geterrno() +{ + return g_errorNum; +} + +int* geterrnoptr() +{ + return &g_errorNum; +} diff --git a/crt2/src/a_file.c b/crt2/src/a_file.c new file mode 100644 index 00000000..408c4422 --- /dev/null +++ b/crt2/src/a_file.c @@ -0,0 +1,829 @@ +// *************************************************************** +// a_file.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +// Max files open at once -- increase if necessary +#define FD_STDIO 65535 +#define FIMAX 64 +#define EOT 4 // end of transmission, aka Ctrl-D + +static int g_OpenedFileDes[FIMAX]; +static int g_OpenedDirDes [FIMAX]; + +static int FileSpotToFileHandle(int spot) +{ + if (spot < 0 || spot >= FIMAX) return -1; + return g_OpenedFileDes[spot]; +} + +static int DirSpotToFileHandle(int spot) +{ + if (spot < 0 || spot >= FIMAX) return -1; + return g_OpenedDirDes[spot]; +} + +// Check if a file is opened here in this process +bool _I_IsFileOpenedHere(int fd) +{ + for (int i = 0; i < FIMAX; i++) + { + if (g_OpenedFileDes[i] == fd) + return true; + } + return false; +} + +// Check if a directory is opened here in this process +bool _I_IsDirectoryOpenedHere(int dd) +{ + for (int i = 0; i < FIMAX; i++) + { + if (g_OpenedDirDes[i] == dd) + return true; + } + return false; +} + +int open(const char* path, int oflag) +{ + int spot = -1; + + for (int i = 0; i < FIMAX; i++) + { + if (g_OpenedFileDes[i] < 0) + { + spot = i; break; + } + } + + if (spot == -1) + { + SetErrorNumber(-EMFILE); + return -1; + } + + // FiOpenDebug now supports custom paths... + int fd = _I_FiOpenDebug(path, oflag, "usertask", 1); + + if (fd < 0) + { + SetErrorNumber(fd); + return -1; + } + + g_OpenedFileDes[spot] = fd; + return spot; +} + +int close(int spot) +{ + int fd = FileSpotToFileHandle(spot); + if (fd < 0) + { + SetErrorNumber(-EBADF); + return -1; + } + + if (!_I_IsFileOpenedHere(fd)) + { + SetErrorNumber(-EBADF); + return -1; + } + + int rv = _I_FiClose (fd); + + // if closing was successful: + if (rv >= 0) + { + g_OpenedFileDes[spot] = -1; + } + + return rv; +} + +// honestly, this needs to be redone too +int read_stdio(void* buf, unsigned int nbyte) +{ + char* bufchar = buf; + for (unsigned i = 0; i < nbyte; i++) + { + char c = _I_ReadChar(); + *(bufchar++) = c; + + // If we got an 'end of transmission', instantly return + if (c == EOT) + return (int)i; + } + + return nbyte; +} + +int write_stdio(const void* buf, unsigned int nbyte) +{ + const char* bufchar = buf; + char b[2]; + b[1] = 0; + for (unsigned i = 0; i < nbyte; i++) + { + b[0] = *(bufchar++); + _I_PutString(b); + } + + return nbyte; +} + +int read(int spot, void* buf, unsigned int nbyte) +{ + int filedes = FileSpotToFileHandle(spot); + if (filedes < 0) + return SetErrorNumber(-EBADF); + + if (!_I_IsFileOpenedHere(filedes)) + return SetErrorNumber(-EBADF); + + if (filedes == FD_STDIO) + { + return read_stdio(buf, nbyte); + } + + int result = _I_FiRead(filedes, buf, nbyte); + + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int write(int spot, const void* buf, unsigned int nbyte) +{ + int filedes = FileSpotToFileHandle(spot); + if (filedes < 0) + return SetErrorNumber(-EBADF); + + if (!_I_IsFileOpenedHere(filedes)) + return SetErrorNumber(-EBADF); + + if (filedes == FD_STDIO) + { + return write_stdio(buf, nbyte); + } + + int result = _I_FiWrite(filedes, (void*)buf, nbyte); + + if (result < 0) + return SetErrorNumber((int)result); + + return result; +} + +int lseek(int spot, int offset, int whence) +{ + int filedes = FileSpotToFileHandle(spot); + if (filedes < 0) + return SetErrorNumber(-EBADF); + + if (filedes == FD_STDIO) return SetErrorNumber(-ESPIPE); + + if (!_I_IsFileOpenedHere(filedes)) + return SetErrorNumber(-EBADF); + + int result = _I_FiSeek(filedes, offset, whence); + + if (result < 0) + return SetErrorNumber((int)result); + + return result; +} + +int tellf(int spot) +{ + int filedes = FileSpotToFileHandle(spot); + if (filedes < 0) + return SetErrorNumber(-EBADF); + + if (filedes == FD_STDIO) return SetErrorNumber(-ESPIPE); + + if (!_I_IsFileOpenedHere(filedes)) + return SetErrorNumber(-EBADF); + + int result = _I_FiTell(filedes); + + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int tellsz(int spot) +{ + int filedes = FileSpotToFileHandle(spot); + if (filedes < 0) + return SetErrorNumber(-EBADF); + + if (filedes == FD_STDIO) return SetErrorNumber(-ESPIPE); + + if (!_I_IsFileOpenedHere(filedes)) + return SetErrorNumber(-EBADF); + + int result = _I_FiTellSize(filedes); + + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int ioctl(int spot, unsigned long request, void * argp) +{ + int filedes = FileSpotToFileHandle(spot); + if (filedes < 0) + return SetErrorNumber(-EBADF); + + if (filedes == FD_STDIO) + { + // process the IO request directly TODO + return -ENOTTY; + } + + if (!_I_IsFileOpenedHere(filedes)) + return SetErrorNumber(-EBADF); + + int result = _I_FiIoControl(filedes, request, argp); + + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int FiOpenDir(const char* pFileName) +{ + int spot = -1; + + for (int i = 0; i < FIMAX; i++) + { + if (g_OpenedFileDes[i] < 0) + { + spot = i; break; + } + } + + if (spot == -1) + { + SetErrorNumber(-EMFILE); + return -EMFILE; + } + + int fd = _I_FiOpenDirD(pFileName, "[Process]", 1); + + if (fd < 0) + return SetErrorNumber(fd); + + g_OpenedDirDes[spot] = fd; + + return spot; +} + +int FiCloseDir(int spot) +{ + int dd = DirSpotToFileHandle(spot); + if (dd < 0) + return SetErrorNumber(-EBADF); + + if (!_I_IsDirectoryOpenedHere(dd)) + return SetErrorNumber(-EBADF); + + int closeRes = _I_FiCloseDir(dd); + if (closeRes >= 0) + { + for (int i = 0; i < FIMAX; i++) + { + if (g_OpenedDirDes[i] == dd) + g_OpenedDirDes[i] = -1; + } + } + else + return SetErrorNumber(closeRes); + + return closeRes; +} + +int FiReadDir(DirEnt* space, int spot) +{ + int dd = DirSpotToFileHandle(spot); + if (dd < 0) + { + SetErrorNumber(-EBADF); + return -1; + } + + if (!_I_IsDirectoryOpenedHere(dd)) + { + SetErrorNumber(-EBADF); + return -1; + } + + int result = _I_FiReadDir(space, dd); + + if (result >= 0) + return result; + + SetErrorNumber(result); + return -1; +} + +int FiSeekDir(int spot, int loc) +{ + int dd = DirSpotToFileHandle(spot); + if (dd < 0) + return SetErrorNumber(-EBADF); + + if (!_I_IsDirectoryOpenedHere(dd)) + return SetErrorNumber(-EBADF); + + int result = _I_FiSeekDir(dd, loc); + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int FiRewindDir(int spot) +{ + int dd = DirSpotToFileHandle(spot); + if (dd < 0) + return SetErrorNumber(-EBADF); + + if (!_I_IsDirectoryOpenedHere(dd)) + return SetErrorNumber(-EBADF); + + int result = _I_FiRewindDir(dd); + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int FiTellDir(int spot) +{ + int dd = DirSpotToFileHandle(spot); + if (dd < 0) + return SetErrorNumber(-EBADF); + + if (!_I_IsDirectoryOpenedHere(dd)) + return SetErrorNumber(-EBADF); + + int result = _I_FiTellDir(dd); + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int FiStatAt(int spot, const char *pfn, StatResult* pres) +{ + int dd = DirSpotToFileHandle(spot); + if (dd < 0) + return SetErrorNumber(-EBADF); + + if (!_I_IsDirectoryOpenedHere(dd)) + return SetErrorNumber(-EBADF); + + int result = _I_FiStatAt(dd, pfn, pres); + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int FiStat(const char *pfn, StatResult* pres) +{ + int result = _I_FiStat(pfn, pres); + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int FiLinkStat(const char *pfn, StatResult* pres) +{ + int result = _I_FiLinkStat(pfn, pres); + if (result < 0) + return SetErrorNumber(result); + + return result; +} + +int FiChDir(const char *pfn) +{ + int result = _I_FiChDir(pfn); + if (result < 0) + SetErrorNumber(result); + + return result; +} + +// C Standard I/O +FILE* fdopen (int fd, UNUSED const char* type) +{ + if (fd < 0) + return NULL; + + FILE* pFile = calloc(1, sizeof(FILE)); + if (!pFile) + { + close(fd); + return NULL; + } + pFile->fd = fd; + return pFile; +} + +FILE* fopen (const char* file, const char* mode) +{ + const char* mode1 = mode; + int flags = O_CREAT; + + while (*mode) + { + switch (*mode) + { + case'r':case'R':flags |= O_RDONLY;break; + case'w':case'W':flags |= O_WRONLY;break; + case'a':case'A':flags |= O_APPEND;break; + case'+': flags &=~O_CREAT; break; + } + mode++; + } + + return fdopen(open(file, flags), mode1); +} + +int fclose(FILE* file) +{ + if (file->fd == FD_STDIO) return SetErrorNumber(-ESPIPE); + + if (file) + { + int op = close(file->fd); + + if (op < 0) + { + SetErrorNumber(op); + return op; + } + + free (file); + return op; + } + + SetErrorNumber(-EBADF); + return -EBADF; +} + +size_t fread (void* ptr, size_t size, size_t nmemb, FILE* stream) +{ + SetErrorNumber(0); + + size_t nbyte = size * nmemb; + size_t rd = read(stream->fd, ptr, nbyte); + + if (rd < nbyte) + { + // must have reached end of file + stream->eof = true; + } + + if (GetErrorNumber()) + { + stream->error = true; + } + + return rd; +} + +size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) +{ + SetErrorNumber(0); + + size_t nbyte = size * nmemb; + size_t wr = write(stream->fd, ptr, nbyte); + if (wr < nbyte) + { + stream->eof = true; + } + + if (GetErrorNumber()) + { + stream->error = true; + } + + return wr; +} + +int fseek(FILE* file, int offset, int whence) +{ + SetErrorNumber(0); + int rv = lseek(file->fd, offset, whence); + + if (GetErrorNumber()) + { + file->error = true; + } + + return rv; +} + +int ftell(FILE* file) +{ + SetErrorNumber(0); + int rv = tellf(file->fd); + + if (GetErrorNumber()) + { + file->error = true; + } + + return rv; +} + +void rewind(FILE* stream) +{ + fseek(stream, 0, SEEK_SET); +} + +// called on exit +void _I_CloseOpenFiles() +{ + for (int i = 0; i < FIMAX; i++) + { + if (g_OpenedFileDes[i] >= 0) + { + _I_FiClose(g_OpenedFileDes[i]); + g_OpenedFileDes[i] = -1; + } + } +} + +FILE *stdin, *stdout, *stderr; + +// called on start +void _I_Setup() +{ + for (int i = 0; i < FIMAX; i++) + g_OpenedFileDes[i] = g_OpenedDirDes[i] = -1; + + g_OpenedFileDes[0] = FD_STDIO; + g_OpenedFileDes[1] = FD_STDIO; + g_OpenedFileDes[2] = FD_STDIO; + + //well, they're going to be the same file, which is fine. + //Points to file description 0, which is handle FD_STDIO, should be ok. + stdin = stdout = stderr = calloc(1, sizeof(FILE)); + stdin->fd = 0; +} + +int unlink (const char* filename) +{ + return _I_FiUnlinkFile(filename); +} + +int getc (FILE* pFile) +{ + if (pFile->ungetc_buf_sz > 0) + { + return pFile->ungetc_buf[--pFile->ungetc_buf_sz]; + } + + char chr = 0; + fread(&chr, 1, 1, pFile); + if (pFile->eof) + return EOF; + + return chr; +} + +int fgetc(FILE* stream) +{ + return getc(stream); +} + +int ungetc(int c, FILE * stream) +{ + if (stream->ungetc_buf_sz >= (int) sizeof stream->ungetc_buf) + return EOF; + + stream->ungetc_buf[stream->ungetc_buf_sz++] = c; + return 0; +} + +int feof(FILE* f) +{ + return f->eof; +} + +int ferror(FILE* f) +{ + return f->error; +} + +void clearerr(FILE* f) +{ + f->error = 0; +} + +// empty for now. There's no actual flushing to be done. +int fflush(FILE* file) +{ + (void)file; + return 0; +} + +int rename(const char* old, const char* new) +{ + return _I_FiRename(old, new); +} + +int renameat(int olddirspot, const char* old, int newdirspot, const char* new) +{ + //TODO + (void) old; (void) new; + (void) olddirspot; (void) newdirspot; + return -ENOTSUP; +} + +int mkdir(const char * path, UNUSED int mode) +{ + return _I_FiMakeDir(path); +} + +int rmdir(const char * path) +{ + return _I_FiRemoveDir(path); +} + +int FiCreatePipe(const char * friendlyName, int pipefd[2], int flags) +{ + // look for two free spots. + int j = 0; + for (int i = 0; i < FIMAX && j < 2; i++) + { + if (g_OpenedFileDes[i] == -1) + pipefd[j++] = i; + } + + // if j didn't reach 2.. + if (j < 2) + { + //we have too many files open + return -EMFILE; + } + + int kshandles[2]; + int result = _I_FiCreatePipe(friendlyName, kshandles, flags); + + if (result < 0) return result; // actually don't do anything + + // assign the two pipefd's to the kernel handles + g_OpenedFileDes[pipefd[0]] = kshandles[0]; + g_OpenedFileDes[pipefd[1]] = kshandles[1]; + + return 0; +} + +int pipe2(int pipefd[2], int flags) +{ + return FiCreatePipe("pipe", pipefd, flags); +} + +int pipe(int pipefd[2]) +{ + return pipe2(pipefd, 0); +} + +int remove (const char* filename) +{ + StatResult res; + int stat = FiStat(filename, &res); + if (stat < 0) return stat; + + if (res.m_type == FILE_TYPE_DIRECTORY) + { + return _I_FiRemoveDir(filename); + } + else + { + return _I_FiUnlinkFile(filename); + } +} + +void* mmap(void * addr, size_t length, int prot, int flags, int fd, off_t offset) +{ + void* pMem = MAP_FAILED; + + int ec = MemoryMap(addr, length, prot, flags, fd, offset, &pMem); + + if (ec < 0) + { + SetErrorNumber(ec); + return MAP_FAILED; + } + + return pMem; +} + +int munmap(void * addr, size_t sz) +{ + int ec = MemoryUnmap(addr, sz); + if (ec < 0) + SetErrorNumber(ec); + return ec; +} + +char* getcwd(char* buf, size_t sz) +{ + strncpy(buf, FiGetCwd(), sz); + buf[sz - 1] = 0; + return buf; +} + +// Standard C wrappers for directory ops +DIR* opendir(const char* dirname) +{ + int dd = FiOpenDir(dirname); + if (dd < 0) + return NULL; // errno is already set + + DIR* dir = malloc(sizeof(DIR)); + dir->m_DirHandle = dd; + return dir; +} + +int closedir(DIR* dirp) +{ + int result = FiCloseDir(dirp->m_DirHandle); + if (result < 0) + return -1; + + free(dirp); + return 0; +} + +void rewinddir(DIR* dirp) +{ + // seek to the beginning, ie. 0 + FiSeekDir(dirp->m_DirHandle, 0); +} + +int telldir(DIR* dirp) +{ + return FiTellDir(dirp->m_DirHandle); +} + +void seekdir(DIR* dirp, int told) +{ + FiSeekDir(dirp->m_DirHandle, told); +} + +struct dirent* readdir(DIR* dirp) +{ + // keep an offset that we can fill in. + int offset = FiTellDir(dirp->m_DirHandle); + + // if we couldn't grab the telldir result, just bail. FiTellDir set an errno anyways + if (offset < 0) + return NULL; + + // note: It doesn't matter if we skip some entries (. and ..), those will be + // skipped again if we seekdir() to dirent->d_off and readdir() again. + + // perform the actual read + int result = FiReadDir(&dirp->m_NDirEnt, dirp->m_DirHandle); + + // if we got nothing (i.e. either hit the end, or had an error), + // return null. FiReadDir sets an errno if something happened. + if (result != 0) + return NULL; + + // fill out the entries here: + DirEnt* pDirEnt = &dirp->m_NDirEnt; + struct dirent* ptr = &dirp->m_PDirEnt; // posix dirent + + strcpy(ptr->d_name, pDirEnt->m_name); + + ptr->d_reclen = sizeof(*ptr); + ptr->d_ino = pDirEnt->m_inode; + ptr->d_type = pDirEnt->m_type; + ptr->d_off = offset; + + return ptr; +} diff --git a/crt2/src/a_math.c b/crt2/src/a_math.c new file mode 100644 index 00000000..299db36a --- /dev/null +++ b/crt2/src/a_math.c @@ -0,0 +1,60 @@ +// *************************************************************** +// a_math.c - Creation date: 06/12/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +int abs (int a) +{ + if (a < 0) + return -a; + + return a; +} + +double fabs (double x) +{ + if (x < 0) + return -x; + + return x; +} + +static unsigned int s_randomSeed; + +void _I_RandInit() +{ + s_randomSeed = GetRandom(); +} + +// TODO: Replace this with a better RNG. +static unsigned int temper(unsigned int x) +{ + x ^= x >> 11; + x ^= x << 7 & 0x9d2c5680; + x ^= x << 15 & 0xefc60000; + x ^= x >> 18; + return x; +} + +int rand_r(unsigned int * seed) +{ + return (temper(*seed = *seed * 1103515245 + 12345) / 2) & 0x7FFFFFFF; +} + +int rand() +{ + return rand_r(&s_randomSeed); +} + +void srand(unsigned int seed) +{ + s_randomSeed = seed; +} diff --git a/crt2/src/a_mem.c b/crt2/src/a_mem.c new file mode 100644 index 00000000..7135643a --- /dev/null +++ b/crt2/src/a_mem.c @@ -0,0 +1,638 @@ +// *************************************************************** +// a_mem.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +void SLogMsg(const char * c, ...); +void SLogMsgNoCr(const char* fmt, ...); + +// If the free gap has between and + 32 bytes, don't split it. It's kind of a waste. +// If the free gap's size is + 32 bytes and over, proceed to perform a split. +#define C_MEM_ALLOC_TOLERANCE (32) + +// If a newly freed block of memory is larger than 1024 bytes, g_bAddedToLastHeaderHint gets set to +// false.. So new allocations would try that spot first. +#define C_MEM_FREE_LARGE_ENOUGH (4096) + +#define MAGIC_NUMBER_1_USED (0xDDEAFB10) +#define MAGIC_NUMBER_2_USED (0x19960623) +#define MAGIC_NUMBER_1_FREE (0x00000000) +#define MAGIC_NUMBER_2_FREE (0x534F534E) + +#define C_MEMORY_SIZE (0x40000000) // 1 GB. Should you need more, move gMemory down in memory (but avoid going below 0x10000000!!) + +// since MAGIC_NUMBER_1_FREE is zero +#define IS_FREE(header) (!((header)->m_magicNo1)) + +typedef struct MemAreaHeader +{ + uint32_t m_magicNo1; + struct MemAreaHeader* m_pNext; + struct MemAreaHeader* m_pPrev; + size_t m_size; + uint32_t m_magicNo2; +} +MemAreaHeader; + +#define SIZE_AND_LOCATION_PADDING (1 << 2) // 4 + +// The memory area. Prefer imitating old NanoShell's way of doing things. +uint8_t* gMemory = (uint8_t*)0x40000000; + +// The size of the memory area. +const uintptr_t gMemorySize = C_MEMORY_SIZE; + +// 262144 entries, so 512 KB. Not Bad. Stores the amount of memory allocations that use this page. +uint16_t gMemoryPageReference [C_MEMORY_SIZE / 4096]; + +MemAreaHeader *gLastHeader; + +// for optimization, add to the last header if we've been doing that +// Not sure that it optimizes that much actually.. only a 2ms gain at best +bool g_bAddedToLastHeaderHint = false; + +bool MemMgrIsPageUsed(uintptr_t page) +{ + return gMemoryPageReference[page] != 0; +} + +void MemOnOOM(int errCode, bool bUnmappingMemory); + +void MemMgrRequestMemMap(uintptr_t place, size_t size) +{ + void *redundant; + int errorCode = MemoryMap((void*)(place & ~0xFFF), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, &redundant); + + if (errorCode != -ENOTHING) + { + MemOnOOM(errorCode, false); + } +} + +void MemMgrAbort() +{ + abort(); +} + +void MemMgrRequestMemUnMap(uintptr_t place, size_t size) +{ + __attribute__((unused)) int errorCode = + MemoryUnmap((void*)(place & ~0xFFF), size); +} + +// don't use this directly +void MemMgrAddReferenceToPage(uintptr_t page) +{ + gMemoryPageReference[page]++; +} + +// this also +void MemMgrRemoveReferenceToPage(uintptr_t page) +{ + if (gMemoryPageReference[page]-- == 0) + { + gMemoryPageReference[page] = 0; + LogMsg("Tried to free the same page twice? (%p)", page); + abort(); + } +} + +MemAreaHeader* MemMgrSetupMemoryRegion(MemAreaHeader* pHeader, size_t nSize) +{ + pHeader->m_magicNo1 = MAGIC_NUMBER_1_USED; + pHeader->m_magicNo2 = MAGIC_NUMBER_2_USED; + pHeader->m_size = nSize; + + MemAreaHeader* pNext = (MemAreaHeader*)((uint8_t*)pHeader + sizeof *pHeader + nSize); + pHeader->m_pNext = pNext; + pNext ->m_pPrev = pHeader; + pNext ->m_pNext = NULL; + + return pNext; +} + +MemAreaHeader* MemMgrGetInitialHeader() +{ + return (MemAreaHeader*)gMemory; +} + +void MemMgrPerformSanityChecks(MemAreaHeader* pHeader) +{ + // Perform an address check first - make sure we aren't performing any OOB accesses. + uint8_t* pHeaderBytes = (uint8_t*)pHeader; + if (gMemory + gMemorySize <= pHeaderBytes || pHeaderBytes < gMemory) + { + LogMsg("Heap corruption detected! Block %p isn't within the memory area reserved to the program.", pHeader); + MemMgrAbort(); + } + + // Perform a magic number check + if ((pHeader->m_magicNo1 != MAGIC_NUMBER_1_FREE && pHeader->m_magicNo1 != MAGIC_NUMBER_1_USED) || + (pHeader->m_magicNo2 != MAGIC_NUMBER_2_FREE && pHeader->m_magicNo2 != MAGIC_NUMBER_2_USED)) + { + // Uh oh! We have a heap corruption. Report to the user! + LogMsg("Heap corruption detected! Block %p's magic numbers aren't correctly set.", pHeader); + MemMgrAbort(); + } + + // Yep, all good +} + +void MemMgrUseMemoryRegion(void* ptr, size_t sz) +{ + uintptr_t p = (uintptr_t) ptr - (uintptr_t) gMemory; + uintptr_t pageStart = p / 0x1000, pageEnd = (p + sz) / 0x1000; + uintptr_t mmapStreak = 0, pageStartedStreak = 0; + + for (uintptr_t page = pageStart; page <= pageEnd; page++) + { + //if the page isn't used, add to the current streak + if (!MemMgrIsPageUsed(page)) + { + if (mmapStreak++ == 0) + pageStartedStreak = page; + } + //well, it's used, so commit the current changes if we have a streak + else if (mmapStreak) + { + uintptr_t mmapCur = pageStartedStreak * 0x1000 + (uintptr_t) gMemory; + MemMgrRequestMemMap(mmapCur, mmapStreak * 4096); + mmapStreak = 0; + } + MemMgrAddReferenceToPage(page); + } + + //well, in the end we might still have some streak left, so be sure to map that too. + if (mmapStreak) + { + if (!pageStartedStreak) + pageStartedStreak = pageStart; + + uintptr_t mmapCur = pageStartedStreak * 0x1000 + (uintptr_t) gMemory; + MemMgrRequestMemMap(mmapCur, mmapStreak * 4096); + mmapStreak = 0; + } +} + +void MemMgrFreeMemoryRegion(void* ptr, size_t sz) +{ + uintptr_t p = (uintptr_t) ptr - (uintptr_t) gMemory; + uintptr_t pageStart = p / 0x1000, pageEnd = (p + sz) / 0x1000; + uintptr_t mmapStreak = 0, pageStartedStreak = 0; + + for (uintptr_t page = pageStart; page <= pageEnd; page++) + { + //if the page isn't used, add to the current streak + if (!MemMgrIsPageUsed(page)) + { + if (mmapStreak++ == 0) + pageStartedStreak = page; + } + //well, it's used, so commit the current changes if we have a streak + else if (mmapStreak) + { + uintptr_t mmapCur = pageStartedStreak * 0x1000 + (uintptr_t) gMemory; + MemMgrRequestMemUnMap(mmapCur, mmapStreak); + mmapStreak = 0; + } + MemMgrRemoveReferenceToPage(page); + } + + //well, in the end we might still have some streak left, so be sure to unmap that too. + if (mmapStreak) + { + if (!pageStartedStreak) + pageStartedStreak = pageStart; + + uintptr_t mmapCur = pageStartedStreak * 0x1000 + (uintptr_t) gMemory; + MemMgrRequestMemUnMap(mmapCur, mmapStreak * 4096); + mmapStreak = 0; + } +} + +// Initialise the first block. +void MemMgrInitializeMemory() +{ + MemAreaHeader *pHeader = MemMgrGetInitialHeader(); + + MemMgrUseMemoryRegion(pHeader, sizeof (MemAreaHeader)); + gLastHeader = pHeader; + + pHeader->m_magicNo1 = MAGIC_NUMBER_1_FREE; + pHeader->m_magicNo2 = MAGIC_NUMBER_2_FREE; + pHeader->m_pNext = NULL; + pHeader->m_pPrev = NULL; + pHeader->m_size = gMemorySize - sizeof(MemAreaHeader); +} + +// First-fit allocation method. We could also use best-fit, however right now I'd rather not. +void* MemMgrAllocateMemory(size_t sz) +{ + if (sz == 0) + return NULL; + + // Pad the size to four bytes, or one double word. + sz = (sz + SIZE_AND_LOCATION_PADDING - 1) & ~(SIZE_AND_LOCATION_PADDING - 1); + + // TODO OPTIMIZE - Store a pointer to the biggest block that's free right now, or something else like that. + + // Okay, first off, navigate our linked list + MemAreaHeader* pHeader = MemMgrGetInitialHeader(), *pLastHeader = NULL; + + while (pHeader) + { + // Perform a sanity check first, before doing *anything*. Has its reasons. + MemMgrPerformSanityChecks(pHeader); + + // is this even free? + if (IS_FREE(pHeader)) + { + // yeah, simply check if we have a free slot here. + if (pHeader->m_size >= sz) + { + // This is good. Now, do we split this up? + if (pHeader->m_size >= sz + sizeof (MemAreaHeader) + C_MEM_ALLOC_TOLERANCE) + { + // Yes. Proceed to split. + uint8_t* pMem = (uint8_t*)&pHeader[1] + sz; + + // This will be the location of the new memory header. + MemAreaHeader* pNewHdr = (MemAreaHeader*)pMem; + + // Map it in memory. + MemMgrUseMemoryRegion(pNewHdr, sizeof (MemAreaHeader)); + + // Initialize its magic bits. + pNewHdr->m_magicNo1 = MAGIC_NUMBER_1_FREE; + pNewHdr->m_magicNo2 = MAGIC_NUMBER_2_FREE; + pNewHdr->m_size = pHeader->m_size - sizeof(MemAreaHeader) - sz; + pHeader->m_size = sz; + // Link it up with the nodes in between + pNewHdr->m_pNext = pHeader->m_pNext; + if (pHeader->m_pNext) pHeader->m_pNext->m_pPrev = pNewHdr; + pNewHdr->m_pPrev = pHeader; + pHeader->m_pNext = pNewHdr; + + if (pHeader == gLastHeader) + { + gLastHeader = pNewHdr; + g_bAddedToLastHeaderHint = true; + } + else + { + g_bAddedToLastHeaderHint = false; + } + } + else + { + // Don't split it. + + if (pHeader == gLastHeader) + { + gLastHeader = NULL; + g_bAddedToLastHeaderHint = false; + } + } + + // Update this header's magic numbers, to mark this block used. + pHeader->m_magicNo1 = MAGIC_NUMBER_1_USED; + pHeader->m_magicNo2 = MAGIC_NUMBER_2_USED; + + MemMgrUseMemoryRegion(pHeader, sz + sizeof (MemAreaHeader)); + + // return the memory right after the header. + return &pHeader[1]; + } + + // Nope! Simply skip, as done below. + } + + pLastHeader = pHeader; + pHeader = pHeader->m_pNext; + } + + // try to expand. If doesn't work, we have run into an out of memory situation. + uint8_t* pMem = NULL; + + if (pLastHeader) + { + pMem = (uint8_t*)&pLastHeader[1] + pLastHeader->m_size; + } + else + { + LogMsg("ERROR: No pLastHeader? (line %d)", __LINE__); + MemMgrAbort(); + } + + if (pMem + sz >= gMemory + gMemorySize) + { + // yikes! + LogMsg("Out of memory area! (trying to allocate size %d)", sz); + return NULL; + } + + assert(pLastHeader->m_pNext == NULL); + + // This will be the location of the new memory header. + MemAreaHeader* pNewHdr = (MemAreaHeader*)pMem; + + // Map the relevant memory area + MemMgrUseMemoryRegion(pNewHdr, sizeof (MemAreaHeader)); + + // update the bits + pNewHdr->m_magicNo1 = MAGIC_NUMBER_1_USED; + pNewHdr->m_magicNo2 = MAGIC_NUMBER_2_USED; + pNewHdr->m_pPrev = pLastHeader; + pNewHdr->m_pNext = NULL; + pLastHeader->m_pNext = pNewHdr; + pNewHdr->m_size = sz; + + gLastHeader = pNewHdr; + + g_bAddedToLastHeaderHint = true; + + MemMgrUseMemoryRegion(pNewHdr, sz + sizeof (MemAreaHeader)); + + return &pNewHdr[1]; +} + +void MemMgrFreeMemory(void *pMem) +{ + if (pMem == NULL) + return; + + // check the padding first + if (((uintptr_t)pMem & (SIZE_AND_LOCATION_PADDING - 1)) != 0) + { + LogMsg("Error: Address passed in is NOT padded to %d!", SIZE_AND_LOCATION_PADDING); + abort(); + } + + // get the header right before the memory with some cool syntax tricks + MemAreaHeader* pHeader = & (-1)[(MemAreaHeader*)pMem]; + + if (gLastHeader == pHeader) + { + gLastHeader = NULL; + g_bAddedToLastHeaderHint = false; + } + + // Ensure the sanity of this header + MemMgrPerformSanityChecks(pHeader); + + // already free? + if (pHeader->m_magicNo1 == MAGIC_NUMBER_1_FREE && pHeader->m_magicNo2 == MAGIC_NUMBER_2_FREE) + { + LogMsg("ERROR: double free attempt at %p", pMem); + MemMgrAbort(); + } + + // mark it as free + pHeader->m_magicNo1 = MAGIC_NUMBER_1_FREE; + pHeader->m_magicNo2 = MAGIC_NUMBER_2_FREE; + + MemMgrFreeMemoryRegion(pHeader, pHeader->m_size + sizeof(MemAreaHeader)); + + // if it's sufficiently large, we may want to stop allocating right at the end of our heap + if (pHeader->m_size >= C_MEM_FREE_LARGE_ENOUGH) + { + g_bAddedToLastHeaderHint = false; + } + + // try to combine with other free slots + MemAreaHeader* pNext = pHeader->m_pNext; + + // Does it exist, and is it free? + if (pNext && IS_FREE(pNext)) + { + // yeah. Merge this and the next together. + pHeader->m_size += pNext->m_size + sizeof (MemAreaHeader); + pHeader->m_pNext = pNext->m_pNext; + if (pNext->m_pNext) pNext->m_pNext->m_pPrev = pHeader; + + // well, pNext is no longer valid, get rid of its magic numbers. + pNext->m_magicNo1 = 0; + pNext->m_magicNo2 = 0; + + // mark it as unused + MemMgrFreeMemoryRegion(pNext, 1); + } + + MemAreaHeader* pPrev = pHeader->m_pPrev; + + // Does it exist? Is it free? + if (pPrev && IS_FREE(pPrev)) + { + // yeah. Merge this and the previous together. + pPrev->m_size += pHeader->m_size + sizeof (MemAreaHeader); + pPrev->m_pNext = pHeader->m_pNext; + if (pHeader->m_pNext) pPrev->m_pNext->m_pPrev = pPrev; + + // well our pHeader is no longer valid, get rid of its magic numbers. + pHeader->m_magicNo1 = 0; + pHeader->m_magicNo2 = 0; + + // mark it as unused + MemMgrFreeMemoryRegion(pHeader, 1); + } +} + +/* +void* MemMgrReAllocateMemory(void* pMem, size_t size) +{ + if (pMem == NULL) + pMem = NULL; + + if (!pMem) + return MemMgrAllocateMemory(size); + + // get the header right before the memory with some cool syntax tricks + MemAreaHeader* pHeader = & (-1)[(MemAreaHeader*)pMem]; + + // Ensure the sanity of this header + MemMgrPerformSanityChecks(pHeader); + + void* pNewMem = MemMgrAllocateMemory(size); + if (!pNewMem) + return NULL; + + size_t minSize = pHeader->m_size; + if (minSize > size) + minSize = size; + + memcpy(pNewMem, pMem, minSize); + + MemMgrFreeMemory(pMem); + + return pNewMem; +} +*/ + +void* MemMgrReAllocateMemory(void* pMem, size_t size) +{ + // get the header right before the memory with some cool syntax tricks + MemAreaHeader* pHeader = & (-1)[(MemAreaHeader*)pMem]; + + // Ensure the sanity of this header + MemMgrPerformSanityChecks(pHeader); + + if (gLastHeader == pHeader) + { + gLastHeader = NULL; + g_bAddedToLastHeaderHint = false; + } + + // Actually, is the size the same? + if (size == pHeader->m_size) + { + return pMem; + } + + // Are we trying to shrink this memory region? This case is trivial. + if (size < pHeader->m_size) + { + // Temporarily add a separate reference to the memory of this header. + // This will ensure that no memory gets removed when we remove the + // other references later. + MemMgrUseMemoryRegion(pHeader, size + sizeof(MemAreaHeader)); + + // Get rid of the references from the old header. + MemMgrFreeMemoryRegion(pHeader, pHeader->m_size + sizeof(MemAreaHeader)); + + // Update the size of this header. + pHeader->m_size = size; + + return pMem; + } + + // TODO: fix this code + /* + + // How much space do we have between this and the next header? + MemAreaHeader* pNext = pHeader->m_pNext; + + if ((uintptr_t)(pNext) >= (uintptr_t)pHeader + sizeof(MemAreaHeader) + size) + { + // Yes. Expand this area using the same method as above for shrinking. + MemMgrUseMemoryRegion(pHeader, size + sizeof(MemAreaHeader)); + + // Get rid of the references from the old header. + MemMgrFreeMemoryRegion(pHeader, pHeader->m_size + sizeof(MemAreaHeader)); + + // Update the size of this header. + pHeader->m_size = size; + + return pMem; + } + + */ + + // We can't! Means we need to relocate. + void * pNewMem = MemMgrAllocateMemory(size); + if (!pNewMem) return NULL; + + memcpy(pNewMem, pMem, pHeader->m_size); + + MemMgrFreeMemory(pMem); + + return pNewMem; +} + +void MemMgrDebugDump() +{ + MemAreaHeader* pHeader = MemMgrGetInitialHeader(); + + while (pHeader) + { + // Perform a sanity check first, before doing *anything*. Has its reasons. + MemMgrPerformSanityChecks(pHeader); + + SLogMsg("Header: %p. Next: %p, Prev: %p. Size: %u.", pHeader, pHeader->m_pNext, pHeader->m_pPrev, pHeader->m_size); + + pHeader = pHeader->m_pNext; + } + + SLogMsg("Memory page reference: "); + + for (int i = 0; i < 50; i++) + SLogMsgNoCr("%x", gMemoryPageReference[i]); + + SLogMsgNoCr("\n"); +} + +void MemMgrCleanup() +{ + // I think we can just do this. Pages whose page mapping isn't set are ignored. + MemMgrRequestMemUnMap((uintptr_t)gMemory, gMemorySize); +} + +// On out of memory +void MemOnOOM(int errCode, bool bUnmappingMemory) +{ + LogMsg("ERROR: ran out of memory (?). Was%s unmapping, error code: %d. Will now quit.", bUnmappingMemory ? "" : "n't", errCode); + + abort(); +} + +void _I_FreeEverything() +{ + MemMgrCleanup(); +} + +void* realloc (void * ptr, size_t size) +{ + if (!ptr) return malloc(size); + + return MemMgrReAllocateMemory(ptr, size); +} + +void *malloc (size_t sz) +{ + void* ptr = MemMgrAllocateMemory(sz); + + // if we have obtained some memory, scrub it + if (ptr) memset(ptr, 0x69, sz); + + return ptr; +} + +void *calloc (size_t nmemb, size_t size) +{ + void *ptr = MemMgrAllocateMemory(nmemb * size); + + if (ptr == NULL) return ptr; + + memset(ptr, 0, nmemb * size); + return ptr; +} + +void free (void* pMem) +{ + if (!pMem) return; + + return MemMgrFreeMemory(pMem); +} + +// Allocating kernel regions, if needed. Will cause a memory leak if not disposed of properly, so use carefully!!! +void* MmKernelAllocate(size_t sz) +{ + return _I_AllocateDebug(sz, __FILE__, __LINE__); +} + +void MmKernelFree(void *pData) +{ + _I_Free(pData); +} + +void * MmKernelReAllocate(void* ptr, size_t sz) +{ + return _I_ReAllocateDebug(ptr, sz, __FILE__, __LINE__); +} diff --git a/crt2/src/a_printf.c b/crt2/src/a_printf.c new file mode 100644 index 00000000..2272dda1 --- /dev/null +++ b/crt2/src/a_printf.c @@ -0,0 +1,566 @@ +// *************************************************************** +// a_printf.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +int fwrite(const void* ptr, size_t sz, size_t nmemb, FILE* stream); +extern FILE* stdout; + +#if SIZE_MAX == 0xFFFFFFFFFFFFFFFFull +#define IS_64_BIT 1 +#else +#define IS_64_BIT 0 +#endif + +// printf implementation + +void uns_to_str(uint64_t num, char* str, int paddingInfo, char paddingChar) +{ + // print the actual digits themselves + int i = 0; + while (num || i == 0) + { + str[i++] = '0' + (num % 10); + str[i] = '\0'; + num /= 10; + } + + // append padding too + for (; i < paddingInfo; ) + { + str[i++] = paddingChar; + str[i] = '\0'; + } + + // reverse the string + int start = 0, end = i - 1; + while (start < end) + { + char + temp = str[start]; + str[start] = str[end]; + str[end] = temp; + start++; + end--; + } +} +void int_to_str(int64_t num, char* str, int paddingInfo, char paddingChar) +{ + if (num < 0) + { + str[0] = '-'; + uns_to_str((uint64_t)(-num), str + 1, paddingInfo, paddingChar); + } + else + uns_to_str((uint64_t) num, str, paddingInfo, paddingChar); +} + +// features: +// %s, %S = prints a string +// %c, %C = prints a char +// %d, %i, %D, %I = prints an int32 +// %u, %U = prints a uint32 +// %l = prints a uint64 +// %L = prints an int64 +// %x, %X = prints a uint32 in hex in lowercase or uppercase respectively +// %q, %Q = prints a uint64 in hex in lowercase or uppercase respectively +// %b, %B = prints a uint16 in hex in lowercase or uppercase respectively +// %w, %W = prints a uint8 in hex in lowercase or uppercase respectively +// %p, %P = prints a pointer address (fully platform dependent) + +int vsnprintf(char* buf, size_t sz, const char* fmt, va_list args) +{ + int paddingInfo = -1; + char paddingChar = ' '; + size_t currentIndex = 0; + while (*fmt) + { + char m = *fmt; + if (!m) goto finished; + fmt++; + + if (m == '%') + { + m = *(fmt++); + + // if hit end, return + if (!m) goto finished; + + // handle %0 or %. + if (m == '0' || m == '.') + { + // this by default handles %0 too, though it does nothing + paddingInfo = 0; + m = *(fmt++); + + // if hit end, return + if (!m) goto finished; + + // handle %0D cases (D = digit) + if (m >= '0' && m <= '9') + { + paddingInfo = m - '0'; + paddingChar = '0'; + m = *(fmt++); + } + } + else if (m >= '1' && m <= '9') + { + paddingInfo = m - '0'; + paddingChar = ' '; + m = *(fmt++); + + // if hit end, return + if (!m) goto finished; + } + + switch (m) + { + // Format a string + case 's': case 'S': + { + const char* pString = va_arg(args, const char*); + + //allow user to print null + if (pString == NULL) + pString = "(null)"; + + while (*pString) + { + if (currentIndex >= sz - 1) + goto finished; + + // place this character here + buf[currentIndex++] = *pString; + + pString++; + } + + break; + } + // Escape a percentage symbol + case '%': + { + if (currentIndex >= sz - 1) + goto finished; + + buf[currentIndex++] = '%'; + break; + } + // Format a char + case 'c': case 'C': + { + // using va_arg(args, char) has undefined behavior, because + // char arguments will be promoted to int. + char character = (char)va_arg(args, int); + + if (currentIndex >= sz - 1) + goto finished; + + buf[currentIndex++] = character; + break; + } + // Format an int + case 'd': case 'i': case 'D': case 'I': + { + int num = va_arg(args, int); + char buffer[20]; + + int_to_str(num, buffer, paddingInfo, paddingChar); + + const char* pString = buffer; + while (*pString) + { + if (currentIndex >= sz - 1) + goto finished; + + // place this character here + buf[currentIndex++] = *pString; + + pString++; + } + + break; + } + // Format an unsigned int + #if !IS_64_BIT + case 'z': + { + m = *(fmt++); + if (m == 0) goto finished; + //fallthrough intended + } + #endif + case 'u': case 'U': + { + uint32_t num = va_arg(args, uint32_t); + char buffer[20]; + + uns_to_str(num, buffer, paddingInfo, paddingChar); + + const char* pString = buffer; + while (*pString) + { + if (currentIndex >= sz - 1) + goto finished; + + // place this character here + buf[currentIndex++] = *pString; + + pString++; + } + + break; + } + // Format a longer integer. + #if IS_64_BIT + case 'z': + #endif + case 'l': + { + bool longlong = false; + #if IS_64_BIT + if (m == 'z') + { + longlong = true; + m = 'u'; + goto __parse_the_thing; + } + #endif + m = *(fmt++); + if (m == 'l') + { + longlong = true; + m = *(fmt++); + } + if (m == 0) goto finished; + + #if IS_64_BIT + __parse_the_thing: + #endif + + const char* pString = NULL; + char buffer[30]; + buffer[0] = 0; + pString = buffer; + + if (m == 'u') + { + unsigned long long num = longlong ? va_arg(args, unsigned long long) : va_arg(args, unsigned long); + uns_to_str(num, buffer, paddingInfo, paddingChar); + } + if (m == 'd') + { + long long num = longlong ? va_arg(args, long long) : va_arg(args, long); + int_to_str(num, buffer, paddingInfo, paddingChar); + } + + if (!pString) break; + + while (*pString) + { + if (currentIndex >= sz - 1) + goto finished; + + // place this character here + buf[currentIndex++] = *pString; + + pString++; + } + break; + } + // Format a uint8_t as lowercase/uppercase hexadecimal + case 'b': + case 'B': + { + const char* charset = "0123456789abcdef"; + if (m == 'B') + charset = "0123456789ABCDEF"; + + // using va_arg(args, uint8_t) has undefined behavior, because + // uint8_t arguments will be promoted to int. + uint8_t p = (uint8_t) va_arg(args, uint32_t); + + if (currentIndex >= sz - 1) goto finished; + buf[currentIndex++] = charset[(p & 0xF0) >> 4]; + if (currentIndex >= sz - 1) goto finished; + buf[currentIndex++] = charset[p & 0x0F]; + + break; + } + // Format a uint16_t as lowercase/uppercase hexadecimal + case 'w': + case 'W': + { + const char* charset = "0123456789abcdef"; + if (m == 'W') + charset = "0123456789ABCDEF"; + + // using va_arg(args, uint16_t) has undefined behavior, because + // uint16_t arguments will be promoted to int. + uint16_t p = (uint16_t) va_arg(args, uint32_t); + + for (uint32_t mask = 0xF000, bitnum = 12; mask; mask >>= 4, bitnum -= 4) + { + if (currentIndex >= sz - 1) + goto finished; + + // place this character here + buf[currentIndex++] = charset[(p & mask) >> bitnum]; + } + + break; + } + // Format a uint32_t as lowercase/uppercase hexadecimal + case 'x': + case 'X': +#if !IS_64_BIT + case 'p': case 'P': +#endif + { + const char* charset = "0123456789abcdef"; + if (m == 'X' || m == 'P') + charset = "0123456789ABCDEF"; + + uint32_t p = va_arg(args, uint32_t); + + for (uint32_t mask = 0xF0000000, bitnum = 28; mask; mask >>= 4, bitnum -= 4) + { + if (currentIndex >= sz - 1) + goto finished; + + // place this character here + buf[currentIndex++] = charset[(p & mask) >> bitnum]; + } + + break; + } + // Format a uint64_t as lowercase/uppercase hexadecimal + case 'q': + case 'Q': +#if IS_64_BIT + case 'p': case 'P': +#endif + { + const char* charset = "0123456789abcdef"; + if (m == 'Q' || m == 'P') + charset = "0123456789ABCDEF"; + + uint64_t p = va_arg(args, uint64_t); + + for (uint64_t mask = 0xF000000000000000ULL, bitnum = 60; mask; mask >>= 4, bitnum -= 4) + { + if (currentIndex >= sz - 1) + goto finished; + + // place this character here + buf[currentIndex++] = charset[(p & mask) >> bitnum]; + } + + break; + } + } + } + else + { + if (currentIndex >= sz - 1) + goto finished; + buf[currentIndex++] = m; + } + } +finished: + buf[currentIndex] = '\0'; + return (int)currentIndex; +} + +int vsprintf(char* buf, const char* fmt, va_list args) +{ + return vsnprintf(buf, SIZE_MAX, fmt, args); +} + +int snprintf(char* buf, size_t sz, const char* fmt, ...) +{ + va_list lst; + va_start(lst, fmt); + + int val = vsnprintf(buf, sz, fmt, lst); + + va_end(lst); + + return val; +} + +int sprintf(char* buf, const char* fmt, ...) +{ + va_list lst; + va_start(lst, fmt); + + int val = vsprintf(buf, fmt, lst); + + va_end(lst); + + return val; +} + +// This is a hack. I may remove it. + +void _I_WritePort(short port, char c) +{ + __asm__("outb %1, %0\n\t"::"d"(port),"a"(c)); +} + +void _I_SPutString(const char* s) +{ + while (*s) + { + _I_WritePort(0xE9, *s); + s++; + } +} + +void LogMsg(const char* fmt, ...) +{ + char cr[8192]; + va_list list; + va_start(list, fmt); + vsnprintf(cr, sizeof(cr) - 2, fmt, list); + + snprintf(cr + strlen(cr), 2, "\n"); + _I_PutString(cr); + + va_end(list); +} + +void SLogMsg(const char* fmt, ...) +{ + char cr[8192]; + va_list list; + va_start(list, fmt); + vsnprintf(cr, sizeof(cr) - 2, fmt, list); + + snprintf(cr + strlen(cr), 2, "\n"); + _I_SPutString(cr); + + va_end(list); +} + +void SLogMsgNoCr(const char* fmt, ...) +{ + char cr[8192]; + va_list list; + va_start(list, fmt); + vsnprintf(cr, sizeof(cr) - 1, fmt, list); + _I_SPutString(cr); + + va_end(list); +} + +void LogMsgNoCr(const char* fmt, ...) +{ + char cr[8192]; + va_list list; + va_start(list, fmt); + vsnprintf(cr, sizeof(cr), fmt, list); + + _I_PutString(cr); + + va_end(list); +} + +int printf(const char* fmt, ...) +{ + char cr[8192]; + va_list list; + va_start(list, fmt); + vsnprintf(cr, sizeof(cr), fmt, list); + + _I_PutString(cr); + + va_end(list); + return strlen(cr); +} + +int vfprintf(FILE* file, const char* fmt, va_list list) +{ + char cr[8192]; + vsnprintf(cr, sizeof(cr), fmt, list); + + int slen = strlen(cr); + int result = fwrite(cr, 1, slen, file); + + return result; +} + +int fprintf(FILE* file, const char* fmt, ...) +{ + va_list list; + va_start(list, fmt); + + int res = vfprintf(file, fmt, list); + + va_end(list); + + return res; +} + +int fputs(const char* s, FILE * stream) +{ + size_t sz = strlen(s); + return fwrite(s, 1, sz, stream); +} + +int fputc(int c, FILE * stream) +{ + char chr = (char)c; + return fwrite(&chr, 1, 1, stream); +} + +int puts(const char * s) +{ + _I_PutString(s); + _I_PutString("\n"); + return strlen(s) + 1; +} + +int putc(int c, FILE* stream) +{ + return fputc(c, stream); +} + +int putchar(int c) +{ + return fputc(c, stdout); +} + +void vprintf(const char* fmt, va_list list) +{ + vfprintf(stdout, fmt, list); +} + +const char* strerror(int errnum); +int GetErrorNumber(); + +void perror(const char* fmt, ...) +{ + char cr[8192]; + va_list list; + va_start(list, fmt); + vsnprintf(cr, sizeof(cr), fmt, list); + + _I_PutString(cr); + + va_end(list); + + // print the error now: + _I_PutString(": "); + _I_PutString(strerror(GetErrorNumber())); + _I_PutString("\n"); +} diff --git a/crt2/src/a_sort.c b/crt2/src/a_sort.c new file mode 100644 index 00000000..84e8d00b --- /dev/null +++ b/crt2/src/a_sort.c @@ -0,0 +1,65 @@ +// *************************************************************** +// a_sort.c - Creation date: 29/12/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** +#include "crtlib.h" + +// TODO: Implement a more efficient sorting algorithm. We will just use bubble sorting +// for now, since the spec doesn't say what algorithm we should be using. + +static __attribute__((always_inline)) +void swap_char(char * c, char * d) +{ + char tmp = *c; + *c = *d; + *d = tmp; +} + +void qsort(void *pBase, size_t nCount, size_t nElementSize, ComparisonFunc pCompare) +{ + for (size_t i = 0; i < nCount; i++) + { + void *p1 = (void *)((uintptr_t)pBase + i * nElementSize); + + for (size_t j = i + 1; j < nCount; j++) + { + void *p2 = (void *)((uintptr_t)pBase + j * nElementSize); + if (pCompare(p1, p2) <= 0) + continue; + + // swap p1 and p2 + char *p1_bytes = (char *)p1; + char *p2_bytes = (char *)p2; + + for (size_t k = 0; k < nElementSize; k++) + swap_char(&p1_bytes[k], &p2_bytes[k]); + } + } +} + +void qsort_r(void *pBase, size_t nCount, size_t nElementSize, ComparisonReentrantFunc pCompareReentrant, void* pArgument) +{ + for (size_t i = 0; i < nCount; i++) + { + void *p1 = (void *)((uintptr_t)pBase + i * nElementSize); + + for (size_t j = i + 1; j < nCount; j++) + { + void *p2 = (void *)((uintptr_t)pBase + j * nElementSize); + if (pCompareReentrant(p1, p2, pArgument) <= 0) + continue; + + // swap p1 and p2 + char *p1_bytes = (char *)p1; + char *p2_bytes = (char *)p2; + + for (size_t k = 0; k < nElementSize; k++) + swap_char(&p1_bytes[k], &p2_bytes[k]); + } + } +} diff --git a/crt2/src/a_string.c b/crt2/src/a_string.c new file mode 100644 index 00000000..18055e24 --- /dev/null +++ b/crt2/src/a_string.c @@ -0,0 +1,949 @@ +// *************************************************************** +// a_string.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" +#include + +// Character operations + +int isalnum(int c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); +} + +int isalpha(int c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +int isascii(int c) +{ + return (c >= 0 && c < 0x80); +} + +int isblank(int c) +{ + return (c == ' ' || c == '\n' || c == '\0'); +} + +int iscntrl(int c) +{ + return (c >= 0 && c <= 0x1F); +} + +int isdigit(int c) +{ + return (c >= '0' && c <= '9'); +} + +int isgraph(int c) +{ + return (c >= '!' && c <= 0x7E); +} + +int islower(int c) +{ + return (c >= 'a' && c <= 'z'); +} + +int isprint(int c) +{ + return (c >= ' ' && c <= 0x7E); +} + +int isspace(int c) +{ + return (c == ' ' || c == '\n'); +} + +int isupper(int c) +{ + return (c >= 'A' && c <= 'Z'); +} + +int isxdigit(int c) +{ + return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'); +} + +int tolower (int c) +{ + if (isupper(c)) return c + ('a' - 'A'); else return c; +} + +int toupper (int c) +{ + if (islower(c)) return c - ('a' - 'A'); else return c; +} + +// Memory operations. + +int memcmp(const void* ap, const void* bp, size_t size) +{ + const char* a = (const char*) ap; + const char* b = (const char*) bp; + for (size_t i = 0; i < size; i++) + { + if (a[i] != b[i]) + return a[i] - b[i]; + } + return 0; +} + +void* memmove(void* dstptr, const void* srcptr, size_t size) +{ + uint8_t* dst = dstptr; + const uint8_t* src = srcptr; + + // test for overlap + if (src + size > dst || dst + size > src) + { + // overlap found. depending on the direction, copy a different way + if (dst < src) + { + while (size--) + *dst++ = *src++; + } + //move backwards + else + { + dst += size; + src += size; + + while (size--) + *(--dst) = *(--src); + } + } + else + { + while (size--) + *dst++ = *src++; + } + + return dstptr; +} + +void* memcpy(void* dstptr, const void* srcptr, size_t size) +{ + // simply copy: + uint8_t* dst = dstptr; + const uint8_t* src = srcptr; + + while (size--) + *dst++ = *src++; + + return dstptr; +} + +void* memset(void* bufptr, int val, size_t size) +{ + uint8_t* buf = bufptr; + while (size--) + *buf++ = val; + + return bufptr; +} + +// String operations. + +size_t strgetlento(const char* str, char chr) +{ + size_t len = 0; + while (str[len] != chr) + { + len++; + } + return len; +} + +int atoi(const char* str) +{ + int f = 0; + int s = 1; + int i = 0; + if (str[0] == '-') + { + i++; + s = -1; + } + for (; str[i] != '\0'; i++) + f = f * 10 + str[i] - '0'; + + return s * f; +} + +long atol(const char* str) +{ + long f = 0; + long s = 1; + long i = 0; + if (str[0] == '-') + { + i++; + s = -1; + } + for (; str[i] != '\0'; i++) + f = f * 10 + str[i] - '0'; + + return s * f; +} + +long long atoll(const char* str) +{ + long long f = 0; + long long s = 1; + long long i = 0; + if (str[0] == '-') + { + i++; + s = -1; + } + for (; str[i] != '\0'; i++) + f = f * 10 + str[i] - '0'; + + return s * f; +} + +size_t strlen(const char* str) +{ + size_t len = 0; + while (*str++) + len++; + + return len; +} + +char* strcpy(char* ds, const char* ss) +{ + char* dso = ds; + + while (*ss) + { + *ds++ = *ss++; + } + + return dso; +} + +void strtolower(char* as) +{ + while(*as != 0) + { + if(*as >= 'A' && *as <= 'Z') + *as += ('a' - 'A'); + as++; + } +} + +void strtoupper(char* as) +{ + while(*as != 0) + { + if(*as >= 'a' && *as <= 'z') + *as -= ('a' - 'A'); + as++; + } +} + +void memtolower(char* as, int w) +{ + int a = 0; + while(a <= w) + { + if(*as >= 'A' && *as <= 'Z') + *as += ('a' - 'A'); + as++; + a++; + } +} + +void memtoupper(char* as, int w) +{ + int a = 0; + while(a <= w) + { + if(*as >= 'a' && *as <= 'z') + *as -= ('a' - 'A'); + as++; + a++; + } +} + +int strcmp(const char* s1, const char* s2) +{ + while (true) + { + // If we've reached the end in both cases, they're equal + if (!*s1 && !*s2) + return 0; + + // If either one of these pointers points to a null character, + // it's fine. + if (*s1 != *s2) + return *s1 - *s2; + + s1++, s2++; + } +} + +char* strcat(char* dest, const char* after) +{ + char* destold = dest; + + // go to the end + while (*dest++); + + // strcpy the src string over + strcpy(dest, after); + + return destold; +} + +char* strchr (const char* str, int c); + +char* Tokenize (TokenState* pState, char* pString, char* separator) +{ + int len, i; + + if(!pState->m_bInitted) + { + pState->m_bInitted = 1; + pState->m_pContinuation = NULL; + pState->m_pReturnValue = NULL; + } + else if (!pState->m_pContinuation) + { + return NULL; + } + + if(!pString) + { + pString = pState->m_pContinuation; + } + + if (!pString) + { + return NULL; + } + + len = strlen (pString); + + for (i = 0; i < len; i++) + { + if (strchr(separator, pString[i])) + { + pState->m_pContinuation = &pString[i+1]; + pString[i] = 0; + pState->m_pReturnValue = pString; + return pString; + } + } + + // not found, hit null term: + pState->m_pContinuation = NULL; + pState->m_pReturnValue = pString; + return pString; +} + +void *malloc (size_t size); + +char *strdup (const char *pText) +{ + size_t len = strlen (pText) + 1; + char *p = malloc (len); + if (p) + memcpy (p, pText, len); + return p; +} + +char * strncpy(char *dst, const char *src, size_t n) +{ + size_t i; + for (i = 0; i < n && src[i] != '\0'; i++) + dst[i] = src[i]; + for (; i < n; i++) + dst[i] = '\0'; + return dst; +} + +size_t strlcpy(char *dst, const char *src, size_t n) +{ + n--; + size_t i; + size_t srclen = 0; + for (i = 0; i < n && src[i] != '\0'; i++) + { + dst[i] = src[i]; + srclen++; + } + dst[i] = '\0'; + + // traverse the rest of src to get its length: + while (src[i++]) + srclen++; + + return srclen; +} + +char* ltoa(long ival, char* buffer, int radix) +{ + assert(radix > 1 && radix < 37); + const char* lut = "0123456789abcdefghijklmnopqrstuvwxyz"; + + char temp[50]; + int i = 0; + + unsigned long value = (unsigned long)ival; + + if (ival < 0) + { + value = -ival; + *buffer++ = '-'; + } + + do + { + temp[i++] = lut[value % radix]; + value /= radix; + } + while (value); + + // store it as reversed + for (int j = 0; j < i; j++) + buffer[j] = temp[i - j - 1]; + + return buffer; +} + +char* itoa(int value, char* buffer, int radix) +{ + return ltoa(value, buffer, radix); +} + +static char* strchr_i(const char* s, int c, bool bReturnNulPos) +{ + // promotion to 'char' intended + char* sc = (char*) s; + + while (*sc) + { + if (*sc == c) return sc; + + sc++; + } + + if (c == 0) + { + return sc; + } + + // assert(*sc == 0); + + return bReturnNulPos ? sc : NULL; +} + +char* strchr(const char* s, int c) +{ + return strchr_i(s, c, false); +} + +char* strchrnul(const char* s, int c) +{ + return strchr_i(s, c, true); +} + +char* strrchr(const char* s, int c) +{ + // promotion to 'char' intended + char* sc = (char*) s; + while (*sc) sc++; + + while (sc != s) + { + sc--; + if (*sc == c) return sc; + } + + return NULL; +} + +int atox(const char* str) +{ + int f = 0; + int s = 1; + int i = 0; + if (str[0] == '-') + { + i++; + s = -1; + } + for (; str[i] != '\0'; i++) + { + f = f * 16; + if (str[i] >= 'a' && str[i] <= 'f') + f += str[i] - 'a' + 0xa; + else if (str[i] >= 'A' && str[i] <= 'F') + f += str[i] - 'A' + 0xA; + else + f += str[i] - '0'; + } + + return s * f; +} + +size_t strnlen(const char* ptext, size_t n) +{ + size_t k = 0; + while (*ptext) + { + k++; + if (k == n) return n; + ptext++; + } + return k; +} + +int strncmp(const char *s1, const char *s2, register size_t n) +{ + unsigned char u1, u2; + while (n-- > 0) + { + u1 = (unsigned char) *s1++; + u2 = (unsigned char) *s2++; + if (u1 != u2) + return u1 - u2; + if (u1 == '\0') + return 0; + } + return 0; +} + +char* strstr(const char *haystack, const char *needle) +{ + const size_t len_nd = strlen(needle); + const size_t len_hs = strlen(haystack); + + if (len_nd > len_hs) + return NULL; // needle is too big to be in the haystack + + if (len_nd == len_hs) + { + if (strcmp(haystack, needle) == 0) + return (char*)haystack; + return NULL; + } + + const size_t len = len_hs - len_nd; + + for (size_t i = 0; i < len; i++) + { + if (strncmp(haystack + i, needle, len_nd) == 0) + return (char*)haystack + i; + } + + return NULL; +} + +size_t strspn(const char* s, const char* accept) +{ + const uint8_t* str = (const uint8_t*)s; + const uint8_t* acc = (const uint8_t*)accept; + + bool lut[256]; // could be a bitset, but nah, not right now + memset (&lut, 0, sizeof lut); + while (*acc) + { + // cast to unsigned because negative indices aren't a thing + lut[*acc++] = 1; + } + + size_t matching = 0; + + while (*str) + { + if (!lut[*str]) + return matching; + matching++; + str++; + } + + return matching; +} + +size_t strcspn(const char* s, const char* reject) +{ + const uint8_t* str = (const uint8_t*)s; + const uint8_t* rej = (const uint8_t*)reject; + + bool lut[256]; // could be a bitset, but nah, not right now + memset (&lut, 0, sizeof lut); + while (*rej) + { + // cast to unsigned because negative indices aren't a thing + lut[*rej++] = 1; + } + + size_t matching = 0; + + while (*str) + { + if (lut[*str]) + return matching; + matching++; + str++; + } + + return matching; +} + +void* memchr(const void* s, int c, size_t n) +{ + const uint8_t* ptr = s; + uint8_t match = c; + + while (n--) + { + if (*ptr == match) + return (void*)ptr; + + ptr++; + } + + return NULL; +} + +void* memrchr(const void* s, int c, size_t n) +{ + const uint8_t* ptr = s; + uint8_t match = c; + + ptr += n; + + while (n--) + { + ptr--; + + if (*ptr == match) + return (void*)ptr; + } + + return NULL; +} + +void* rawmemchr(const void* s, int c) +{ + const uint8_t* ptr = s; + uint8_t match = c; + + while (true) + { + if (*ptr == match) + return (void*)ptr; + + ptr++; + } +} + +double atof(const char *arr) +{ + double value = 0; + bool decimal = 0; + double scale = 1; + int negative = 0; + + // parse the negative sign + if (*arr == '-') + { + arr++; + negative = 1; + } + + while (*arr) + { + // if we're parsing the decimal part + if (decimal) + { + scale /= 10; + value += (*arr - '0') * scale; + } + else + { + if (*arr == '.') + decimal = 1; + else + value = value * 10.0 + (*arr - '0'); + } + arr++; + } + + if (negative) value = -value; + + return value; +} + +#include +#include +#include + +// String to Unsigned X. +unsigned long long strtoux(const char* str, char ** endptr, int base, unsigned long long max) +{ + errno = 0; + unsigned long long val = 0; + + // skip white space + while (*str && isspace(*str)) str++; + if (*str == '\0') + { + //well, we haven't really found any numbers + if (endptr) + *endptr = (char*)str; + errno = EINVAL; + return 0; + } + + // *str isn't zero, so surely *(str+1) can be accessed. Check it + if (*str == '0' && (str[1] == 'x' || str[1] == 'X')) + { + if (base == 0 || base == 16) + { + base = 16; + str += 2; + } + else + { + // well, base wasn't either 16 or zero and we've still got the 0x prefix. + // Set endptr to point to 'x' and return + if (endptr) + *endptr = (char*)&str[1]; + return 0; + } + } + // if we're starting with zero, we're handling octal + else if (*str == '0') + { + if (base == 0 || base == 8) + base = 8; + str++; + } + + // if base is STILL zero, then we're dealing with decimal. + if (base == 0) base = 10; + + // keep reading digits + while (*str) + { + int digit = 0; + if (*str >= '0' && *str <= '9') + digit = *str - '0'; + else if (*str >= 'A' && *str <= 'Z') + digit = *str - 'A' + 10; + else if (*str >= 'a' && *str <= 'z') + digit = *str - 'a' + 10; + else + // we've reached the end. + break; + + // check in what situation we are about to overflow. + + // max without the last digit. + unsigned long long maxwld = max / base; + // if the value exceeds the max without the last digit already, this means that any digit we add will make it fail. + if (val > maxwld) + { + val = max; + errno = ERANGE; + if (endptr) *endptr = (char*)str; + return val; + } + // if it's equal + if (val == maxwld) + { + int digthresh = max % base; + + // if we're trying to tack on a digit bigger than the max's last digit, then we're going to overflow, so don't + if (digit > digthresh) + { + val = max; + errno = ERANGE; + if (endptr) *endptr = (char*)str; + return val; + } + } + + // if it's none of these, whatever digit we add will be valid. + val = val * base + digit; + + str++; + } + + if (endptr) + *endptr = (char*)str; + + return val; +} + +// String to Signed X. +long long strtox(const char* str, char ** endptr, int base, long long max) +{ + errno = 0; + + long long val = 0; long long sign = 1; + long long min = -max - 1; + + // skip white space + while (*str && isspace(*str)) str++; + if (*str == '\0') + { + //well, we haven't really found any numbers + if (endptr) + *endptr = (char*)str; + errno = EINVAL; + return 0; + } + + // if we have a sign, treat it + if (*str == '+' || *str == '-') + { + sign = (*str == '-') ? (-1) : (1); + str++; + } + + // *str isn't zero, so surely *(str+1) can be accessed. Check it + if (*str == '0' && (str[1] == 'x' || str[1] == 'X')) + { + if (base == 0 || base == 16) + { + base = 16; + str += 2; + } + else + { + // well, base wasn't either 16 or zero and we've still got the 0x prefix. + // Set endptr to point to 'x' and return + if (endptr) + *endptr = (char*)&str[1]; + return 0; + } + } + // if we're starting with zero, we're handling octal + else if (*str == '0') + { + if (base == 0 || base == 8) + base = 8; + str++; + } + + // if base is STILL zero, then we're dealing with decimal. + if (base == 0) base = 10; + + // keep reading digits + while (*str) + { + int digit = 0; + if (*str >= '0' && *str <= '9') + digit = *str - '0'; + else if (*str >= 'A' && *str <= 'Z') + digit = *str - 'A' + 10; + else if (*str >= 'a' && *str <= 'z') + digit = *str - 'a' + 10; + else + // we've reached the end. + break; + + // check in what situation we are about to overflow. + + // max without the last digit. + long long maxwld = max / base; + // if the value exceeds the max without the last digit already, this means that any digit we add will make it fail. + if (val > maxwld) + { + val = sign < 0 ? min : max; + errno = ERANGE; + if (endptr) *endptr = (char*)str; + return val; + } + // if it's equal + if (val == maxwld) + { + int digthresh = max % base; + // if we're in the negatives, bump it up + if (sign < 0) digthresh++; + + // if we're trying to tack on a digit bigger than the max's last digit, then we're going to overflow, so don't + if (digit > digthresh) + { + val = sign < 0 ? min : max; + errno = ERANGE; + if (endptr) *endptr = (char*)str; + return val; + } + } + + // if it's none of these, whatever digit we add will be valid. + val = val * base + digit; + + str++; + } + + if (endptr) + *endptr = (char*)str; + + return (long long) val * sign; +} + +unsigned long long int strtoull(const char* str, char ** endptr, int base) +{ + return strtoux(str, endptr, base, ULLONG_MAX); +} + +unsigned long int strtoul(const char* str, char ** endptr, int base) +{ + return (unsigned long int)strtoux(str, endptr, base, ULONG_MAX); +} + +long long int strtoll(const char* str, char ** endptr, int base) +{ + return strtox(str, endptr, base, LLONG_MAX); +} + +long int strtol(const char* str, char ** endptr, int base) +{ + return (long int)strtox(str, endptr, base, LONG_MAX); +} + +double ldexp(UNUSED double val, UNUSED int exp) +{ + // TODO + ASSERT(!"ldexp used!"); + return 0.0; +} + +double strtod(UNUSED const char* str, UNUSED char** endptr) +{ + // TODO + ASSERT(!"strtod used!"); + return 0.0; +} + +long double strtold(UNUSED const char* str, UNUSED char** endptr) +{ + // TODO + ASSERT(!"strtold used!"); + return 0.0; +} + +float strtof(UNUSED const char* str, UNUSED char** endptr) +{ + // TODO + ASSERT(!"strtof used!"); + return 0.0f; +} diff --git a/crt2/src/a_time.c b/crt2/src/a_time.c new file mode 100644 index 00000000..f098f1e3 --- /dev/null +++ b/crt2/src/a_time.c @@ -0,0 +1,141 @@ +// *************************************************************** +// a_video.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +static THREAD_LOCAL struct tm s_tm; + +void sleep(int ms) +{ + TmSleep(ms); +} + +// This function tries to approximate unix time the best that it can. +// This does not take into account leap seconds. Despite that, it matches the time +// functions on my system (used gmtime, matches exactly with a call to time(NULL)) +static int s_daysPerMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +int GetEpochTimeFromTimeStruct(TimeStruct* ts) +{ + int year = ts->year - 1970; + int days = 365 * year + 1; + + // (almost) every 4 years since 1972 are leap years. Count them in + // This should work again. (year - 2 + 4) / 4 = (year - 2) / 4 + 1. + int leapYears = (year + 2) / 4; + days += leapYears; + + // based on the month, determine the day + for (int month = 1; month < ts->month; month++) + { + days += s_daysPerMonth[month]; + if (month == 2 && ts->year % 4 == 0) // February -- leap year + days++; + } + + return days * 86400 + ts->hours * 3600 + ts->minutes * 60 + ts->seconds; +} + +int GetEpochTime() +{ + return GetEpochTimeFromTimeStruct(GetTime()); +} + +void GetHumanTimeFromEpoch(int utime, TimeStruct* pOut) +{ + // Separate the amount of days from the rest of the time. + int days = utime / 86400, secs = utime % 86400; + + // Turn the secs into hours, minutes and seconds. This is trivial. + pOut->hours = secs / 3600; + pOut->minutes = secs / 60 % 60; + pOut->seconds = secs % 60; + + // Get the year number. + int year = 1970; + while (days >= 365 + (year % 4 == 0)) + { + days -= 365 + (year % 4 == 0); + year++; + } + + // Get the month number. + int month = 1; + while (days >= s_daysPerMonth[month]) + { + days -= s_daysPerMonth[month]; + month++; + } + + pOut->day = days + 1; + pOut->month = month; + pOut->year = year; +} + +void StructTmToTimeStruct(TimeStruct* out, struct tm* in) +{ + out->seconds = in->tm_sec; + out->minutes = in->tm_min; + out->hours = in->tm_hour; + out->weekday = in->tm_wday; + out->day = in->tm_mday; + out->month = in->tm_mon; + out->year = in->tm_year; + out->statusA = out->statusB = 0; +} + +void TimeStructToStructTm(TimeStruct* in, struct tm* out) +{ + out->tm_sec = in->seconds; + out->tm_min = in->minutes; + out->tm_hour = in->hours; + out->tm_wday = in->weekday; + out->tm_mday = in->day; + out->tm_mon = in->month; + out->tm_year = in->year; + out->tm_gmtoff = 0; + out->tm_zone = "GMT"; +} + +double difftime(time_t a, time_t b) +{ + return a - b; +} + +time_t time(time_t* timer) +{ + time_t t = GetEpochTimeFromTimeStruct(GetTime()); + + if (timer) + *timer = t; + + return t; +} + +time_t mktime (struct tm * ptr) +{ + TimeStruct ts; + StructTmToTimeStruct(&ts, ptr); + return GetEpochTimeFromTimeStruct(&ts); +} + +struct tm* localtime_r(const time_t* timep, struct tm * result) +{ + TimeStruct ts; + GetHumanTimeFromEpoch(*timep, &ts); + TimeStructToStructTm(&ts, result); + return result; +} + +struct tm* localtime(const time_t* timep) +{ + return localtime_r(timep, &s_tm); +} diff --git a/crt2/src/a_ver.c b/crt2/src/a_ver.c new file mode 100644 index 00000000..49a221f4 --- /dev/null +++ b/crt2/src/a_ver.c @@ -0,0 +1,30 @@ +// *************************************************************** +// a_ver.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +char g_VersionString[10] = "VX.XX"; + +const char* GetVersionString() +{ + if (g_VersionString[1] == 'X') + { + int ver = NsGetVersion(); + //major version and minor version: + //NanoShell V1.00 (when that comes out) will have a version number of 100 + //Current version as of Feb 10,2022 (NanoShell V0.30) has a version code of 30. + //Some software may naively just put a 0 in the major version number, but + //we should expect an eventual V1.00 or more. + sprintf(g_VersionString, "V%d.%02d", ver/100, ver%100); + } + + return g_VersionString; +} diff --git a/crt2/src/a_video.c b/crt2/src/a_video.c new file mode 100644 index 00000000..bdfd82aa --- /dev/null +++ b/crt2/src/a_video.c @@ -0,0 +1,59 @@ +// *************************************************************** +// a_video.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +int GetWidth (Rectangle* rect) +{ + return rect->right - rect->left; +} + +int GetHeight (Rectangle* rect) +{ + return rect->bottom - rect->top; +} + +void VidDrawRectangle(unsigned color, Rectangle rect) +{ + VidDrawRect(color, rect.left, rect.top, rect.right, rect.bottom); +} + +void VidFillRectangle(unsigned color, Rectangle rect) +{ + VidFillRect(color, rect.left, rect.top, rect.right, rect.bottom); +} + +bool RectangleContains(Rectangle*r, Point*p) +{ + return (r->left <= p->x && r->right >= p->x && r->top <= p->y && r->bottom >= p->y); +} + +bool RectangleOverlap(Rectangle *r1, Rectangle *r2) +{ + return (r1->left <= r2->right && r1->right >= r2->left && r1->top <= r2->bottom && r1->bottom >= r2->top); +} + +void VidSetClipRect(Rectangle* x) +{ + if (x) + { + VidSetClipRectP(*x); + } + else + { + Rectangle r; + r.left = -1; + r.top = -1; + r.right = -1; + r.bottom = -1; + VidSetClipRectP(r); + } +} diff --git a/crt2/src/a_window.c b/crt2/src/a_window.c new file mode 100644 index 00000000..f4cf90a8 --- /dev/null +++ b/crt2/src/a_window.c @@ -0,0 +1,17 @@ +// *************************************************************** +// a_window.c - Creation date: 01/09/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +void _I_CloseOpenWindows() +{ + //... +} diff --git a/crt2/src/calldefs.h b/crt2/src/calldefs.h new file mode 100644 index 00000000..fffabd15 --- /dev/null +++ b/crt2/src/calldefs.h @@ -0,0 +1,606 @@ +// *************************************************************** +// calldefs.h - Creation date: 21/04/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +// Video Call Functions: +CALL (GetScreenSizeX, VID_GET_SCREEN_WIDTH, int) + RARGS() +CALL_END +CALL (GetScreenSizeY, VID_GET_SCREEN_HEIGHT, int) + RARGS() +CALL_END +CALL (VidPlotPixel, VID_PLOT_PIXEL, void, unsigned x, unsigned y, unsigned color) + SARGS(x,y,color) +CALL_END +CALL (VidFillScreen, VID_FILL_SCREEN, void, unsigned color) + SARGS(color) +CALL_END +CALL (VidDrawVLine, VID_DRAW_V_LINE, void, unsigned color, int top, int bottom, int x) + SARGS(color,top,bottom,x) +CALL_END +CALL (VidDrawHLine, VID_DRAW_H_LINE, void, unsigned color, int left, int right, int y) + SARGS(color,left,right,y) +CALL_END +CALL (VidDrawLine, VID_DRAW_LINE, void, unsigned p, int x1, int y1, int x2, int y2) + SARGS(p,x1,y1,x2,y2) +CALL_END +CALL (VidSetFont, VID_SET_FONT, void, unsigned fontType) + SARGS(fontType) +CALL_END +CALL (VidPlotChar, VID_PLOT_CHAR, void, char c,unsigned ox,unsigned oy,unsigned colorFg,unsigned colorBg) + SARGS(c,ox,oy,colorFg,colorBg) +CALL_END +CALL (VidBlitImage, VID_BLIT_IMAGE, void, Image*pImage, int x, int y) + SARGS(pImage,x,y) +CALL_END +CALL (VidTextOut, VID_TEXT_OUT, void, const char* pText, unsigned ox, unsigned oy, unsigned colorFg, unsigned colorBg) + SARGS(pText,ox,oy,colorFg,colorBg) +CALL_END +CALL (VidTextOutInternal, VID_TEXT_OUT_INT, void, const char* pText, unsigned ox, unsigned oy, unsigned colorFg, unsigned colorBg, bool doNotActuallyDraw, int* width, int* height) + SARGS(pText,ox,oy,colorFg,colorBg,doNotActuallyDraw,width,height) +CALL_END +CALL (VidDrawText, VID_DRAW_TEXT, void, const char* pText, Rectangle rect, unsigned drawFlags, unsigned colorFg, unsigned colorBg) + SARGS(pText, rect, drawFlags, colorFg, colorBg) +CALL_END +CALL (VidShiftScreen, VID_SHIFT_SCREEN, void, int amount) + SARGS(amount) +CALL_END +CALL (VidFillRect, VID_FILL_RECT, void, unsigned color, int left, int top, int right, int bottom) + SARGS(color,left,top,right,bottom) +CALL_END +CALL (VidDrawRect, VID_DRAW_RECT, void, unsigned color, int left, int top, int right, int bottom) + SARGS(color,left,top,right,bottom) +CALL_END +CALL (VidFillRectHGradient, VID_FILL_RECT_H_GRADIENT, void, unsigned colorL, unsigned colorR, int left, int top, int right, int bottom) + SARGS(colorL,colorR,left,top,right,bottom) +CALL_END +CALL (VidFillRectVGradient, VID_FILL_RECT_V_GRADIENT, void, unsigned colorU, unsigned colorD, int left, int top, int right, int bottom) + SARGS(colorU,colorD,left,top,right,bottom) +CALL_END + +// Window Call Functions: +CALL (CreateWindow, WIN_CREATE, Window*, const char*pTitle, int xPos, int yPos, int xSize, int ySize, WindowProc proc, int flags) + RARGS(pTitle, xPos, yPos, xSize, ySize, proc, flags) +CALL_END + +CALL (DestroyWindow, WIN_DESTROY, void, Window* pWindow) + SARGS(pWindow) +CALL_END + +CALL (DefaultWindowProc, WIN_DEFAULT_PROC, void, Window* pWindow, int messageType, int parm1, int parm2) + SARGS(pWindow, messageType, parm1, parm2) +CALL_END + +CALL (MessageBox, WIN_MESSAGE_BOX, int, Window* pWindow, const char* pText, const char* pCaption, uint32_t type) + RARGS(pWindow, pText, pCaption, type) +CALL_END + +CALL (AddControl, WIN_ADD_CONTROL, int, Window* pWindow, int type, Rectangle rect, const char* text, int comboID, int p1, int p2) + RARGS(pWindow, type, rect, text, comboID, p1, p2) +CALL_END + +CALL (HandleMessages, WIN_HANDLE_MESSAGES, bool, Window* pWindow) + RARGS(pWindow) +CALL_END + +// Window stuff +CALL (RequestRepaint, WIN_REQUEST_REPAINT, void, Window* pWindow) + SARGS(pWindow) +CALL_END +CALL (SetLabelText, WIN_SET_LABEL_TEXT, void, Window* pWindow, int comboID, const char* pText) + SARGS(pWindow, comboID, pText) +CALL_END +CALL (AddMenuBarItem, WIN_ADD_MENUBAR_ITEM, void, Window* pWindow, int menuBarControlId, int comboIdTo, int comboIdAs, const char* pText) + SARGS(pWindow, menuBarControlId, comboIdTo, comboIdAs, pText) +CALL_END +CALL (SetScrollBarMin, WIN_SET_SCROLL_BAR_MIN, void, Window *pWindow, int comboID, int min) + SARGS(pWindow, comboID, min) +CALL_END +CALL (SetScrollBarMax, WIN_SET_SCROLL_BAR_MAX, void, Window *pWindow, int comboID, int max) + SARGS(pWindow, comboID, max) +CALL_END +CALL (SetScrollBarPos, WIN_SET_SCROLL_BAR_POS, void, Window *pWindow, int comboID, int pos) + SARGS(pWindow, comboID, pos) +CALL_END +CALL (GetScrollBarPos, WIN_GET_SCROLL_BAR_POS, int, Window *pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL (AddElementToList, WIN_ADD_ELEM_TO_LIST, void, Window* pWindow, int comboID, const char* pText, int optionalIcon) + SARGS(pWindow, comboID, pText, optionalIcon) +CALL_END +CALL (RemoveElementFromList, WIN_REM_ELEM_FROM_LIST, void, Window* pWindow, int comboID, int elemIndex) + SARGS(pWindow, comboID, elemIndex) +CALL_END +CALL (GetElementStringFromList, WIN_GET_ELEM_STR_FROM_LIST, const char*, Window* pWindow, int comboID, int elemIndex) + RARGS(pWindow, comboID, elemIndex) +CALL_END +CALL (ResetList, WIN_CLEAR_LIST, void, Window* pWindow, int comboID) + SARGS(pWindow, comboID) +CALL_END + +// Console I/O +CALLI(PutString, CON_PUTSTRING, void, const char* pText) + SARGS(pText) +CALL_END +CALLI(ReadChar, CON_READCHAR, char, void) + RARGS() +CALL_END +CALLI(ReadString, CON_READSTR, void, char* pOutBuffer, int maxSize) + SARGS(pOutBuffer, maxSize) +CALL_END + +// Memory allocation +CALLI(AllocateDebug, MM_ALLOCATE_D, void*, size_t size, const char* callerFile, int callerLine) + RARGS(size, callerFile, callerLine) +CALL_END +CALLI(Free, MM_FREE, void, void* ptr) + SARGS(ptr) +CALL_END +CALLI(MmDebugDump, MM_DEBUG_DUMP, void, void) + SARGS() +CALL_END + +// File I/O +CALLI(FiOpenDebug, FI_OPEN_D, int /* file descriptor or errcode if negative */, const char* pFileName, int oFlag, const char* pSrcFile, int nSrcLine) + RARGS(pFileName, oFlag, pSrcFile, nSrcLine) +CALL_END +CALLI(FiClose, FI_CLOSE, int /* err code */, int fd) + RARGS(fd) +CALL_END +CALLI(FiRead, FI_READ, int /* num bytes read */, int fd, void* pBuf, int nBytes) + RARGS(fd, pBuf, nBytes) +CALL_END +CALLI(FiWrite, FI_WRITE, int /* num bytes read */, int fd, void* pBuf, int nBytes) + RARGS(fd, pBuf, nBytes) +CALL_END +CALLI(FiTell, FI_TELL, int /* num bytes into file */, int fd) + RARGS(fd) +CALL_END +CALLI(FiTellSize, FI_TELLSIZE, int /* num bytes into file */, int fd) + RARGS(fd) +CALL_END +CALLI(FiSeek, FI_SEEK, int /* err code */, int fd, int offset, int whence) + RARGS(fd, offset, whence) +CALL_END + +CALL (SetHugeLabelText, WIN_SET_HUGE_LABEL_TEXT, void, Window* pWindow, int comboID, const char* pText) + SARGS(pWindow, comboID, pText) +CALL_END +CALL (SetTextInputText, WIN_SET_INPUT_TEXT_TEXT, void, Window* pWindow, int comboID, const char* pText) + SARGS(pWindow, comboID, pText) +CALL_END +CALL (SetWindowIcon, WIN_SET_WINDOW_ICON, void, Window* pWindow, int iconID) + SARGS(pWindow, iconID) +CALL_END +CALL (SetWindowTitle, WIN_SET_WINDOW_TITLE, void, Window* pWindow, const char* pText) + SARGS(pWindow, pText) +CALL_END + +CALL (GetTickCount, TM_GET_TICK_COUNT, int, void) + RARGS() +CALL_END +CALL (GetTime, TM_GET_TIME, TimeStruct*, void) + RARGS() +CALL_END +CALL (GetCpuType, CPU_GET_TYPE, const char*, void) + RARGS() +CALL_END +CALL (GetCpuName, CPU_GET_NAME, const char*, void) + RARGS() +CALL_END + +CALL (GetConsole, CON_GET_CURRENT_CONSOLE, void*, void) + RARGS() +CALL_END + +CALL (RegisterEvent, WIN_REGISTER_EVENT, void, Window* pWindow, short evType, int parm1, int parm2) + SARGS(pWindow, evType, parm1, parm2) +CALL_END +CALL (RegisterEventInsideWndProc, WIN_REGISTER_EVENT2, void, Window* pWindow, short evType, int parm1, int parm2) + SARGS(pWindow, evType, parm1, parm2) +CALL_END + +CALL (VidBlitImageResize, VID_BLIT_IMAGE_RESIZE, void, Image*pImage, int x, int y, int width, int height) + SARGS(pImage,x,y, width, height) +CALL_END + +CALL (TmSleep, TM_SLEEP, void, int ms) + SARGS(ms) +CALL_END + +CALL (SetIcon, WIN_SET_ICON, void, Window* pWindow, int comboID, int icon) + SARGS(pWindow,comboID,icon) +CALL_END + +CALL (NsGetVersion, NS_GET_VERSION, int, void) + RARGS() +CALL_END + +CALL (GetThemingParameter, WIN_GET_THEME_PARM, uint32_t, int type) + RARGS(type) +CALL_END +CALL (SetThemingParameter, WIN_SET_THEME_PARM, void, int type, uint32_t parm) + SARGS(type, parm) +CALL_END + +// Calls V1.3 +CALL (CheckboxSetChecked, WIN_CHECKBOX_SET_CHECKED, void, Window* pWindow, int comboID, bool checked) + SARGS(pWindow, comboID, checked) +CALL_END +CALL (CheckboxGetChecked, WIN_CHECKBOX_GET_CHECKED, bool, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL (TextInputQueryDirtyFlag, WIN_TEXT_INPUT_QUERY_DIRTY_FLAG, bool, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL (TextInputClearDirtyFlag, WIN_TEXT_INPUT_CLEAR_DIRTY_FLAG, void, Window* pWindow, int comboID) + SARGS(pWindow, comboID) +CALL_END +CALL (TextInputGetRawText, WIN_TEXT_INPUT_GET_RAW_TEXT, const char*, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALLI(CcRunCCode, CC_RUN_C_CODE, int, const char* pData, int length) + RARGS(pData, length) +CALL_END +CALLI(FiUnlinkFile, FI_REMOVE_FILE, int, const char* pName) + RARGS(pName) +CALL_END +CALL (AddControlEx, WIN_ADD_CONTROL_EX, int, Window* pWindow, int type, int amode, Rectangle rect, const char* text, int comboID, int p1, int p2) + RARGS(pWindow, type, amode, rect, text, comboID, p1, p2) +CALL_END +CALL (RequestRepaintNew, WIN_REQUEST_REPAINT_NEW, void, Window* pWindow) + SARGS(pWindow) +CALL_END +CALL (ShellAbout, WIN_SHELL_ABOUT, void, const char* text, int icon) + SARGS(text, icon) +CALL_END +CALL (InputBox, WIN_INPUT_BOX, char*, Window* pWindow, const char* pPrompt, const char* pCaption, const char* pDefaultText) + RARGS(pWindow, pPrompt, pCaption, pDefaultText) +CALL_END +CALL (ColorInputBox, WIN_COLOR_BOX, uint32_t, Window* pWindow, const char* pPrompt, const char* pCaption) + RARGS(pWindow, pPrompt, pCaption) +CALL_END +CALL (PopupWindow, WIN_POPUP_WINDOW, void, Window* pWindow, const char* newWindowTitle, int newWindowX, int newWindowY, int newWindowW, int newWindowH, WindowProc newWindowProc, int newFlags) + SARGS(pWindow, newWindowTitle, newWindowX, newWindowY, newWindowW, newWindowH, newWindowProc, newFlags) +CALL_END +CALL (FilePickerBox, WIN_FILE_CHOOSE_BOX, char *, Window * pWindow, const char * prompt, const char * caption, const char * default_text) + RARGS(pWindow, prompt, caption, default_text) +CALL_END + +// Calls V1.4 +CALL (VidSetVbeData, VID_SET_VBE_DATA, VBEData*, VBEData* pData) + RARGS(pData) +CALL_END +CALLI(FiOpenDirD, FI_OPEN_DIR_D, int, const char* pFileName, const char* srcFile, int srcLine) + RARGS(pFileName, srcFile, srcLine) +CALL_END +CALLI(FiCloseDir, FI_CLOSE_DIR, int, int dd) + RARGS(dd) +CALL_END +CALLI(FiReadDir, FI_READ_DIR, int, DirEnt* p, int dd) + RARGS(p, dd) +CALL_END +CALLI(FiSeekDir, FI_SEEK_DIR, int, int dd, int loc) + RARGS(dd, loc) +CALL_END +CALLI(FiRewindDir, FI_REWIND_DIR, int, int dd) + RARGS(dd) +CALL_END +CALLI(FiTellDir, FI_TELL_DIR, int, int dd) + RARGS(dd) +CALL_END +CALLI(FiStatAt, FI_STAT_AT, int, int dd, const char *pfn, StatResult* pres) + RARGS(dd, pfn, pres) +CALL_END +CALLI(FiStat, FI_STAT, int, const char *pfn, StatResult* pres) + RARGS(pfn, pres) +CALL_END +CALL (FiGetCwd, FI_GET_CWD, const char*, void) + RARGS() +CALL_END +CALLI(FiChDir, FI_CHANGE_DIR, int, const char* p) + RARGS(p) +CALL_END +CALL (GetWidgetEventHandler, WIN_GET_WIDGET_EVENT_HANDLER, WidgetEventHandler, int type) + RARGS(type) +CALL_END +CALL (SetWidgetEventHandler, WIN_SET_WIDGET_EVENT_HANDLER, void, Window *pWindow, int comboID, WidgetEventHandler handler) + SARGS(pWindow, comboID, handler) +CALL_END +CALL (SetImageCtlMode, WIN_SET_IMAGE_CTL_MODE, void, Window *pWindow, int comboID, int mode) + SARGS(pWindow, comboID, mode) +CALL_END +CALL (SetImageCtlColor, WIN_SET_IMAGE_CTL_COLOR, void, Window *pWindow, int comboID, uint32_t color) + SARGS(pWindow, comboID, color) +CALL_END +CALL (SetImageCtlCurrentImage, WIN_SET_IMAGE_CTL_IMAGE, void, Window *pWindow, int comboID, Image* pImage) + SARGS(pWindow, comboID, pImage) +CALL_END +CALL (GetImageCtlCurrentImage, WIN_GET_IMAGE_CTL_IMAGE, Image*, Window *pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL (ImageCtlZoomToFill, WIN_IMAGE_CTL_ZOOM_TO_FILL, void, Window *pWindow, int comboID) + SARGS(pWindow, comboID) +CALL_END +CALL (SetFocusedControl, WIN_SET_FOCUSED_CONTROL, void, Window *pWindow, int comboID) + SARGS(pWindow, comboID) +CALL_END +CALL (PopupWindowEx, WIN_POPUP_WINDOW_EX, void, const char* newWindowTitle, int newWindowX, int newWindowY, int newWindowW, int newWindowH, WindowProc newWindowProc, int newFlags, void* newData) + SARGS(newWindowTitle, newWindowX, newWindowY, newWindowW, newWindowH, newWindowProc, newFlags, newData) +CALL_END +CALL (ErrNoStr, ERR_GET_STRING, const char*, int errNum) + RARGS(errNum) +CALL_END +CALL (GetMousePos, VID_GET_MOUSE_POS, Point, void) + RARGS() +CALL_END +CALL (VidSetClipRectP, VID_SET_CLIP_RECT, void, Rectangle r) + SARGS(r) +CALL_END +CALL (CbClear, CB_CLEAR, void, void) + SARGS() +CALL_END +CALL (CbCopyText, CB_COPY_TEXT, bool, const char *pText) + RARGS(pText) +CALL_END +CALL (CbCopyBlob, CB_COPY_BLOB, bool, void* pData, size_t sz) + RARGS(pData, sz) +CALL_END +CALL (CbGetCurrentVariant, CB_GET_CURRENT_VARIANT, ClipboardVariant*, void) + RARGS() +CALL_END +CALL (CbRelease, CB_RELEASE, void, ClipboardVariant* pVar) + SARGS(pVar) +CALL_END + +// Calls V1.5 +CALL (RenderIcon, VID_RENDER_ICON, void, int type, int x, int y) + SARGS(type, x, y) +CALL_END +CALL (RenderIconOutline, VID_RENDER_ICON_OUTLINE, void, int type, int x, int y, uint32_t color) + SARGS(type, x, y, color) +CALL_END +CALL (RenderIconForceSize, VID_RENDER_ICON_SIZE, void, int type, int x, int y, int size) + SARGS(type, x, y, size) +CALL_END +CALL (RenderIconForceSizeOutline, VID_RENDER_ICON_SIZE_OUTLINE, void, int type, int x, int y, int size, uint32_t color) + SARGS(type, x, y, size, color) +CALL_END +CALL (GetRandom, TM_GET_RANDOM, int, void) + RARGS() +CALL_END + +// Calls V1.6 +CALLI(ReAllocateDebug, MM_REALLOCATE_D, void*, void* o, size_t s, const char* f, int l) + RARGS(o, s, f, l) +CALL_END +CALL (ShellExecute, SH_EXECUTE, int, const char* p) + RARGS(p) +CALL_END +CALL (ShellExecuteResource, SH_EXECUTE_RESOURCE, int, const char* p) + RARGS(p) +CALL_END + +// Calls V1.7 +CALL (MemoryMap, MM_MAP_MEMORY_USER, int, void* pMem, size_t nSize, int pFlags, int mFlags, int fd, size_t off, void **pOut) + RARGS(pMem, nSize, pFlags, mFlags, fd, off, pOut) +CALL_END +CALL (MemoryUnmap, MM_UNMAP_MEMORY_USER, int, void* pMem, size_t nSize) + RARGS(pMem, nSize) +CALL_END + +// Calls V1.8 +CALLI(FiRename, FI_RENAME, int, const char * pfnOld, const char * pfnNew) + RARGS(pfnOld, pfnNew) +CALL_END +CALLI(FiMakeDir, FI_MAKE_DIR, int, const char * path) + RARGS(path) +CALL_END +CALLI(FiRemoveDir, FI_REMOVE_DIR, int, const char * path) + RARGS(path) +CALL_END +CALLI(FiCreatePipe, FI_CREATE_PIPE, int, const char * friendlyName, int fds[2], int oflags) + RARGS(friendlyName, fds, oflags) +CALL_END +CALLI(FiIoControl, FI_IO_CONTROL, int, int fd, unsigned long request, void * argp) + RARGS(fd, request, argp) +CALL_END +CALL (CallControlCallback, WIN_CALL_CTL_CALLBACK, void, Window * window, int comboid, int event, int parm1, int parm2) + SARGS(window, comboid, event, parm1, parm2) +CALL_END +CALL (TextInputSetMode, WIN_TEXT_INPUT_SET_MODE, void, Window* pWindow, int comboID, int mode) + SARGS(pWindow, comboID, mode) +CALL_END + +// Calls V1.9 +CALL(GetScrollBarMin, WIN_GET_SCROLL_BAR_MIN, int, Window *pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL(GetScrollBarMax, WIN_GET_SCROLL_BAR_MAX, int, Window *pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL(GetSelectedIndexList, WIN_GET_SEL_INDEX_LIST, int, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL(SetSelectedIndexList, WIN_SET_SEL_INDEX_LIST, void, Window* pWindow, int comboID, int index) + SARGS(pWindow, comboID, index) +CALL_END +CALL(GetSelectedIndexTable, WIN_GET_SEL_INDEX_TABLE, int, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL(SetSelectedIndexTable, WIN_SET_SEL_INDEX_TABLE, void, Window* pWindow, int comboID, int selectedIndex) + SARGS(pWindow, comboID, selectedIndex) +CALL_END +CALL(GetScrollTable, WIN_GET_SCROLL_TABLE, int, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL(SetScrollTable, WIN_SET_SCROLL_TABLE, void, Window* pWindow, int comboID, int scroll) + SARGS(pWindow, comboID, scroll) +CALL_END +CALL(AddTableRow, WIN_ADD_TABLE_ROW, void, Window* pWindow, int comboID, const char* pText[], int optionalIcon) + SARGS(pWindow, comboID, pText, optionalIcon) +CALL_END +CALL(AddTableColumn, WIN_ADD_TABLE_COLUMN, void, Window* pWindow, int comboID, const char* pText, int width) + SARGS(pWindow, comboID, pText, width) +CALL_END +CALL(GetRowStringsFromTable, WIN_GET_ROW_STRINGS_FROM_TABLE, bool, Window* pWindow, int comboID, int index, const char * output[]) + RARGS(pWindow, comboID, index, output) +CALL_END +CALL(RemoveRowFromTable, WIN_REMOVE_ROW_FROM_TABLE, void, Window* pWindow, int comboID, int elementIndex) + SARGS(pWindow, comboID, elementIndex) +CALL_END +CALL(ResetTable, WIN_RESET_TABLE, void, Window* pWindow, int comboID) + SARGS(pWindow, comboID) +CALL_END + +// Calls V2.0 +CALL(VidReadPixel, VID_READ_PIXEL, unsigned, unsigned x, unsigned y) + RARGS(x,y) +CALL_END +CALL(CfgGetString, CFG_GET_STRING, const char*, const char* parm) + RARGS(parm) +CALL_END +CALL(VidGetVbeData, VID_GET_VBE_DATA, VBEData*, void) + RARGS() +CALL_END + +// Calls V2.1 +CALL(GetWindowTitle, WIN_GET_WINDOW_TITLE, const char*, Window* pWindow) + RARGS(pWindow) +CALL_END +CALL(GetWindowData, WIN_GET_WINDOW_DATA, void*, Window* pWindow) + RARGS(pWindow) +CALL_END +CALL(SetWindowData, WIN_SET_WINDOW_DATA, void, Window* pWindow, void* ptr) + SARGS(pWindow, ptr) +CALL_END +CALL(GetWindowRect, WIN_GET_WINDOW_RECT, void, Window* pWindow, Rectangle* pRectOut) + SARGS(pWindow, pRectOut) +CALL_END +CALL(CallWindowCallbackAndControls, WIN_CALL_CALLBACK_AND_CTLS, void, int et, int p1, int p2) + SARGS(et, p1, p2) +CALL_END +CALL(ChangeCursor, WIN_CHANGE_CURSOR, void, Window* pWindow, int cursorID) + SARGS(pWindow, cursorID) +CALL_END +CALL(SetWindowFlags, WIN_SET_FLAGS, void, Window* pWindow, int flags) + SARGS(pWindow, flags) +CALL_END +CALL(GetWindowFlags, WIN_GET_FLAGS, int, Window* pWindow) + RARGS(pWindow) +CALL_END + +// Calls V2.2 +CALL(SetMousePos, VID_SET_MOUSE_POS, void, int newX, int newY) + SARGS(newX, newY) +CALL_END + +CALL(AddTimer, WIN_ADD_TIMER, int, Window* pWindow, int frequency, int event) + RARGS(pWindow, frequency, event) +CALL_END + +CALL(DisarmTimer, WIN_DISARM_TIMER, void, Window* pWindow, int timerID) + SARGS(pWindow, timerID) +CALL_END + +CALL(ChangeTimer, WIN_CHANGE_TIMER, void, Window* pWindow, int timerID, int newFreq, int newEvent) + SARGS(pWindow, timerID, newFreq, newEvent) +CALL_END + +// Calls V2.3 +CALL(UploadCursor, WIN_UPLOAD_CURSOR, int, Image * pImage, int xOff, int yOff) + RARGS(pImage, xOff, yOff) +CALL_END +CALL(ReleaseCursor, WIN_UPLOAD_CURSOR, void, int cursorID) + SARGS(cursorID) +CALL_END +CALL(SetListItemText, WIN_SET_LIST_ITEM_TEXT, void, Window* pWindow, int comboID, int index, int icon, const char * pText) + SARGS(pWindow, comboID, index, icon, pText) +CALL_END +CALL(GetIconImage, WIN_GET_ICON_IMAGE, Image*, int iconID, int size) + RARGS(iconID, size) +CALL_END +CALL(GetResource, RST_LOOK_UP_RESOURCE, Resource*, int resID) + RARGS(resID) +CALL_END +CALL(SetControlDisabled, WIN_SET_CONTROL_DISABLED, void, Window* pWindow, int comboID, bool flag) + SARGS(pWindow, comboID, flag) +CALL_END +CALL(SetControlFocused, WIN_SET_CONTROL_FOCUSED, void, Window* pWindow, int comboID, bool flag) + SARGS(pWindow, comboID, flag) +CALL_END +CALL(SetControlVisible, WIN_SET_CONTROL_VISIBLE, void, Window* pWindow, int comboID, bool flag) + SARGS(pWindow, comboID, flag) +CALL_END +CALL(ProgBarSetProgress, WIN_PROG_BAR_SET_PROGRESS, void, Window* pWindow, int comboID, int x) + SARGS(pWindow, comboID, x) +CALL_END +CALL(ProgBarSetMaxProg, WIN_PROG_BAR_SET_MAX_PROG, void, Window* pWindow, int comboID, int x) + SARGS(pWindow, comboID, x) +CALL_END + +// Calls V2.4 +CALL(ComboBoxAddItem, WIN_COMBO_BOX_ADD_ITEM, void, Window* pWindow, int comboID, const char* item, int itemID, int iconID) + SARGS(pWindow, comboID, item, itemID, iconID) +CALL_END +CALL(ComboBoxGetSelectedItemID, WIN_COMBO_BOX_GET_SELECTED_ITEM, int, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL(ComboBoxSetSelectedItemID, WIN_COMBO_BOX_SET_SELECTED_ITEM, void, Window* pWindow, int comboID, int itemID) + SARGS(pWindow, comboID, itemID) +CALL_END +CALL(ComboBoxClearItems, WIN_COMBO_BOX_CLEAR_ITEMS, void, Window* pWindow, int comboID) + SARGS(pWindow, comboID) +CALL_END +CALL(IsControlFocused, WIN_IS_CONTROL_FOCUSED, bool, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL(IsControlDisabled, WIN_IS_CONTROL_DISABLED, bool, Window* pWindow, int comboID) + RARGS(pWindow, comboID) +CALL_END +CALL(TextInputSetFont, WIN_TEXT_INPUT_SET_FONT, void, Window* pWindow, int comboID, unsigned font) + SARGS(pWindow, comboID, font) +CALL_END +CALL(TextInputRequestCommand, WIN_TEXT_INPUT_REQUEST_COMMAND, void, Window *pWindow, int comboID, int command, void* parm) + SARGS(pWindow, comboID, command, parm) +CALL_END + +// Calls V2.5 +CALL(DrawEdge, WIN_DRAW_EDGE, void, Rectangle rect, int style, unsigned bg) + SARGS(rect, style, bg) +CALL_END +CALL(DrawArrow, WIN_DRAW_ARROW, void, Rectangle rect, eArrowType arrowType, int flags, unsigned color) + SARGS(rect, arrowType, flags, color) +CALL_END +CALL(TabViewAddTab, WIN_TAB_VIEW_ADD_TAB, void, Window* pWindow, int comboID, int tabID, const char* pTabText, int tabWidth) + SARGS(pWindow, comboID, tabID, pTabText, tabWidth) +CALL_END +CALL(TabViewRemoveTab, WIN_TAB_VIEW_REMOVE_TAB, void, Window* pWindow, int comboID, int tabID) + SARGS(pWindow, comboID, tabID) +CALL_END +CALL(TabViewClearTabs, WIN_TAB_VIEW_REMOVE_TAB, void, Window* pWindow, int comboID) + SARGS(pWindow, comboID) +CALL_END +CALL(SpawnMenu, WIN_SPAWN_MENU, Window*, Window* pParentWindow, WindowMenu* pRoot, int x, int y) + RARGS(pParentWindow, pRoot, x, y) +CALL_END +CALL(KbGetKeyState, KB_GET_KEY_STATE, KeyState, unsigned char keycode) + RARGS(keycode) +CALL_END +CALL(LockAcquire, LCK_ACQUIRE, void, SafeLock* ptr) + SARGS(ptr) +CALL_END +CALL(LockFree, LCK_FREE, void, SafeLock* ptr) + SARGS(ptr) +CALL_END + +// Calls V2.6 +CALLI(FiLinkStat, FI_STAT_LINK, int, const char *pfn, StatResult* pres) + RARGS(pfn, pres) +CALL_END diff --git a/crt2/src/calls.c b/crt2/src/calls.c new file mode 100644 index 00000000..c8a6af9b --- /dev/null +++ b/crt2/src/calls.c @@ -0,0 +1,62 @@ +// *************************************************************** +// calls.c - Creation date: 21/04/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" + +#define WINDOW_CALL_BASE 0xc0007c00 + +// Type Definitions. +#define CALL(funcName, funcIndex, retType, ...)\ + typedef retType(* P_I_ ## funcName) (__VA_ARGS__); +#define CALLI(funcName, funcIndex, retType, ...)\ + typedef retType(* P_I_ ## funcName) (__VA_ARGS__); +#define RARGS(...) +#define SARGS(...) +#define CALL_END + +#include "calldefs.h" + +#undef CALL +#undef CALLI +#undef RARGS +#undef SARGS +#undef CALL_END + +// Wrapper function definitions. +// CALL = Function that's available for use. +// CALLI = Internal function that shouldn't be used. +#define CALL(funcName, funcIndex, retType, ...)\ + retType funcName (__VA_ARGS__)\ + {\ + *((uint32_t*)(WINDOW_CALL_BASE + 0xFC)) = funcIndex;\ + P_I_ ## funcName p_func = (P_I_ ## funcName)WINDOW_CALL_BASE;\ + +#define CALLI(funcName, funcIndex, retType, ...)\ + retType _I_ ## funcName (__VA_ARGS__)\ + {\ + *((uint32_t*)(WINDOW_CALL_BASE + 0xFC)) = funcIndex;\ + P_I_ ## funcName p_func = (P_I_ ## funcName)WINDOW_CALL_BASE;\ + +#define RARGS(...)\ + return p_func (__VA_ARGS__);\ + } +#define SARGS(...)\ + p_func (__VA_ARGS__);\ + } +#define CALL_END //empty, use if needed + +#include "calldefs.h" + +#undef CALL +#undef RARGS +#undef SARGS +#undef CALL_END + + diff --git a/crt2/src/crt0.asm b/crt2/src/crt0.asm new file mode 100644 index 00000000..3131215a --- /dev/null +++ b/crt2/src/crt0.asm @@ -0,0 +1,94 @@ +; NanoShell C-Runtime basics +; Copyright (C) 2022 iProgramInCpp + +BITS 32 + +SECTION .data + +; The way we 'exit' on this is a bit unconventional. We do not have a syscall +; to exit, instead, returning from the entry point actually kills the process/thread. +; This is why we need to backup the EBP and ESP, so that we can return properly in +; case of an exit(). +EbpBackup DD 1 +EspBackup DD 1 + +SECTION .text + +; C-main function +EXTERN _CEntry +; Internal library cleanup functions +EXTERN _I_FreeEverything +EXTERN _I_CloseOpenFiles +EXTERN _I_Setup + +; Force exit function `void exit(int code)` +GLOBAL exit +; Internal entry point +GLOBAL _NsStart + +exit: + push ebp + mov ebp, esp + + ; Call internal cleanup functions + call _I_CloseOpenFiles + call _I_FreeEverything + + mov eax, [ebp + 8] + jmp _NsExitPoint ;Nothing to lose + +_NsStart: + mov [EspBackup], esp + mov [EbpBackup], ebp + + push ebp + mov ebp, esp + + mov eax, [ebp + 8] + + push eax + call _CEntry + ; no return + +_NsExitPoint: + mov esp, [EspBackup] + mov ebp, [EbpBackup] + + ret ; return to kernel + +; Well, this is basically the same thing, but a bit more diverse +; https://github.com/jezze/subc +global SetJump +global setjmp +global LongJump +global longjmp +SetJump: +setjmp: + mov edx, [esp + 4] + mov [edx], esp + add dword [edx], 4 + mov [edx + 4], ebp + mov eax, [esp] + mov [edx + 8], eax + mov [edx + 12], ebx + mov [edx + 16], esi + mov [edx + 20], edi + xor eax, eax + retn + +LongJump: +longjmp: + mov eax, [esp + 8] + or eax, eax + ; the spec says to return 1 if there's no 'val' passed into longjmp(). + jnz .increment + inc eax +.increment: + mov edx, [esp + 4] + mov esp, [edx] + mov ebp, [edx + 4] + mov ebx, [edx + 12] + mov esi, [edx + 16] + mov edi, [edx + 20] + mov edx, [edx + 8] + jmp edx diff --git a/crt2/src/crtinternal.h b/crt2/src/crtinternal.h new file mode 100644 index 00000000..63390cb3 --- /dev/null +++ b/crt2/src/crtinternal.h @@ -0,0 +1,32 @@ +// *************************************************************** +// crtinternal.h - Creation date: 21/04/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#ifndef _CRTINTERNAL_H +#define _CRTINTERNAL_H + +#define CALL(funcName, funcIndex, retType, ...) retType funcName (__VA_ARGS__); +#define CALLI(funcName, funcIndex, retType, ...) retType _I_ ## funcName (__VA_ARGS__); +#define RARGS(...) +#define SARGS(...) +#define CALL_END + +#include "calldefs.h" + +#undef CALL +#undef CALLI +#undef RARGS +#undef SARGS +#undef CALL_END + +// currently, this does nothing. However, it's safe to say we will need it when we +// move to a multithreaded version of this libc +#define THREAD_LOCAL + +#endif//_CRTINTERNAL_H \ No newline at end of file diff --git a/crt2/src/crtlib.h b/crt2/src/crtlib.h new file mode 100644 index 00000000..66eaaff4 --- /dev/null +++ b/crt2/src/crtlib.h @@ -0,0 +1,309 @@ +// *************************************************************** +// crtlib.h - Creation date: 21/04/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#ifndef _CRTLIB_H +#define _CRTLIB_H + +#include "../include/nsstructs.h" + +#define WCALL_VERSION 26 +enum +{ + // System Calls V1.0 + CALL_NOTHING, + //Video Driver calls: + VID_GET_SCREEN_WIDTH, + VID_GET_SCREEN_HEIGHT, + VID_PLOT_PIXEL, + VID_FILL_SCREEN, //Actually fills the context, not the screen + VID_DRAW_H_LINE, + VID_DRAW_V_LINE, + VID_DRAW_LINE, + VID_SET_FONT, + VID_PLOT_CHAR, + VID_BLIT_IMAGE, + VID_TEXT_OUT, + VID_TEXT_OUT_INT, + VID_DRAW_TEXT, + VID_SHIFT_SCREEN, + VID_FILL_RECT, + VID_DRAW_RECT, + VID_FILL_RECT_H_GRADIENT, + VID_FILL_RECT_V_GRADIENT, + + //Window Manager calls: + WIN_CREATE, + WIN_HANDLE_MESSAGES, + WIN_DEFAULT_PROC, + WIN_DESTROY, + WIN_MESSAGE_BOX, + WIN_ADD_CONTROL, + + // System Calls V1.1 + WIN_REQUEST_REPAINT, + WIN_SET_LABEL_TEXT, + WIN_ADD_MENUBAR_ITEM, + WIN_SET_SCROLL_BAR_MIN, + WIN_SET_SCROLL_BAR_MAX, + WIN_SET_SCROLL_BAR_POS, + WIN_GET_SCROLL_BAR_POS, + WIN_ADD_ELEM_TO_LIST, + WIN_REM_ELEM_FROM_LIST, + WIN_GET_ELEM_STR_FROM_LIST, + WIN_CLEAR_LIST, + + CON_PUTSTRING, + CON_READCHAR, + CON_READSTR, + + MM_ALLOCATE_D, + MM_FREE, + MM_DEBUG_DUMP, + + FI_OPEN_D, + FI_CLOSE, + FI_READ, + FI_WRITE, + FI_TELL, + FI_TELLSIZE, + FI_SEEK, + + // System Calls V1.2 + WIN_SET_HUGE_LABEL_TEXT, + WIN_SET_INPUT_TEXT_TEXT, + WIN_SET_WINDOW_ICON, + WIN_SET_WINDOW_TITLE, + WIN_REGISTER_EVENT, + WIN_REGISTER_EVENT2, + + TM_GET_TICK_COUNT, + TM_GET_TIME, + + CPU_GET_TYPE, + CPU_GET_NAME, + + CON_GET_CURRENT_CONSOLE, + + //V1.21 + VID_BLIT_IMAGE_RESIZE, + TM_SLEEP, + + //V1.22 + WIN_SET_ICON, + + //V1.23 + NS_GET_VERSION, + + //V1.24 + WIN_GET_THEME_PARM, + WIN_SET_THEME_PARM, + + // System Calls V1.3 + WIN_ADD_CONTROL_EX, + WIN_TEXT_INPUT_QUERY_DIRTY_FLAG, + WIN_TEXT_INPUT_CLEAR_DIRTY_FLAG, + WIN_TEXT_INPUT_GET_RAW_TEXT, + WIN_CHECKBOX_GET_CHECKED, + WIN_CHECKBOX_SET_CHECKED, + + CC_RUN_C_CODE, + + FI_REMOVE_FILE, + + WIN_REQUEST_REPAINT_NEW, + WIN_SHELL_ABOUT, + WIN_INPUT_BOX, + WIN_COLOR_BOX, + WIN_FILE_CHOOSE_BOX,//TODO + WIN_POPUP_WINDOW, + + // System Calls V1.4 + VID_SET_VBE_DATA,//dangerous!! be careful!! + + FI_OPEN_DIR_D, + FI_CLOSE_DIR, + FI_READ_DIR_LEGACY, + FI_SEEK_DIR, + FI_REWIND_DIR, + FI_TELL_DIR, + FI_STAT_AT, + FI_STAT, + FI_GET_CWD, + FI_CHANGE_DIR, + + WIN_GET_WIDGET_EVENT_HANDLER, + WIN_SET_WIDGET_EVENT_HANDLER, + WIN_SET_IMAGE_CTL_MODE, + WIN_SET_IMAGE_CTL_COLOR, + WIN_SET_IMAGE_CTL_IMAGE, + WIN_GET_IMAGE_CTL_IMAGE, + WIN_IMAGE_CTL_ZOOM_TO_FILL, + WIN_SET_FOCUSED_CONTROL, + WIN_POPUP_WINDOW_EX, + + ERR_GET_STRING, + + VID_GET_MOUSE_POS, + VID_SET_CLIP_RECT, + + CB_CLEAR, + CB_COPY_TEXT, + CB_COPY_BLOB, + CB_GET_CURRENT_VARIANT,//danger!! + CB_RELEASE, + + // System Calls V1.5 + VID_RENDER_ICON, + VID_RENDER_ICON_OUTLINE, + VID_RENDER_ICON_SIZE, + VID_RENDER_ICON_SIZE_OUTLINE, + TM_GET_RANDOM, + + // System Calls V1.6 + MM_REALLOCATE_D, + SH_EXECUTE, + SH_EXECUTE_RESOURCE, + + // System Calls V1.7 + MM_MAP_MEMORY_USER, + MM_UNMAP_MEMORY_USER, + + // System Calls V1.8 + FI_RENAME, + FI_MAKE_DIR, + FI_REMOVE_DIR, + FI_CREATE_PIPE, + FI_IO_CONTROL, + WIN_CALL_CTL_CALLBACK, + WIN_TEXT_INPUT_SET_MODE, + + // System Calls V1.9 + WIN_GET_SCROLL_BAR_MIN, + WIN_GET_SCROLL_BAR_MAX, + WIN_GET_SEL_INDEX_LIST, + WIN_SET_SEL_INDEX_LIST, + WIN_GET_SEL_INDEX_TABLE, + WIN_SET_SEL_INDEX_TABLE, + WIN_GET_SCROLL_TABLE, + WIN_SET_SCROLL_TABLE, + WIN_ADD_TABLE_ROW, + WIN_ADD_TABLE_COLUMN, + WIN_GET_ROW_STRINGS_FROM_TABLE, + WIN_REMOVE_ROW_FROM_TABLE, + WIN_RESET_TABLE, + + // System Calls V2.0 + VID_READ_PIXEL, + CFG_GET_STRING, + VID_GET_VBE_DATA, + + // System Calls V2.1 + WIN_GET_WINDOW_TITLE, + WIN_GET_WINDOW_DATA, + WIN_SET_WINDOW_DATA, + WIN_GET_WINDOW_RECT, + WIN_CALL_CALLBACK_AND_CTLS, + WIN_CHANGE_CURSOR, + WIN_SET_FLAGS, + WIN_GET_FLAGS, + + // System Calls V2.2 + VID_SET_MOUSE_POS, + WIN_ADD_TIMER, + WIN_DISARM_TIMER, + WIN_CHANGE_TIMER, + + // System Calls V2.3 + WIN_UPLOAD_CURSOR, + WIN_RELEASE_CURSOR, + WIN_SET_LIST_ITEM_TEXT, + WIN_GET_ICON_IMAGE, + RST_LOOK_UP_RESOURCE, + WIN_SET_CONTROL_DISABLED, + WIN_SET_CONTROL_FOCUSED, + WIN_SET_CONTROL_VISIBLE, + WIN_PROG_BAR_SET_PROGRESS, + WIN_PROG_BAR_SET_MAX_PROG, + + // System Calls V2.4 + WIN_COMBO_BOX_ADD_ITEM, + WIN_COMBO_BOX_GET_SELECTED_ITEM, + WIN_COMBO_BOX_SET_SELECTED_ITEM, + WIN_COMBO_BOX_CLEAR_ITEMS, + WIN_IS_CONTROL_FOCUSED, + WIN_IS_CONTROL_DISABLED, + WIN_TEXT_INPUT_SET_FONT, + WIN_TEXT_INPUT_REQUEST_COMMAND, + + // System Calls V2.5 + WIN_DRAW_EDGE, + WIN_DRAW_ARROW, + WIN_TAB_VIEW_ADD_TAB, + WIN_TAB_VIEW_REMOVE_TAB, + WIN_TAB_VIEW_CLEAR_TABS, + WIN_SPAWN_MENU, + KB_GET_KEY_STATE, + LCK_ACQUIRE, + LCK_FREE, + + // System Calls V2.6 + FI_READ_DIR, + FI_STAT_LINK, +}; + +__attribute__((noreturn)) +void abort (); + +int memcmp (const void* ap, const void* bp, size_t size); +void* memcpy (void* dstptr, const void* srcptr, size_t size); +void* memmove (void* dstptr, const void* srcptr, size_t size); +void* memset (void* bufptr, int val, size_t size); +size_t strlen (const char* str); +char* strcpy (char *dst, const char* src); +char* strncpy (char *dst, const char *src, size_t n); +size_t strlcpy (char *dst, const char *src, size_t n); +int strcmp (const char* as, const char* bs); +int strncmp (const char* s1, const char* s2, size_t n); +char* strcat (char* dest, const char* after); +void strtolower (char* as); +void strtoupper (char* as); +void memtolower (char* as, int w); +void memtoupper (char* as, int w); +size_t strgetlento(const char* str, char chr); +char* strdup (const char *pText); +char* strstr (const char *string, const char *substring); +size_t strnlen (const char* str, size_t szmax); +char* strchr (const char* s, int c); +char* strrchr (const char* s, int c); +char* strchrnul (const char* s, int c); // GNU extension +char* Tokenize (TokenState* pState, char* pString, char* separator); + +void* malloc (size_t size); +void* calloc (size_t nmemb, size_t size); +void free (void* ptr); + +void exit (int num); + +void LogMsg ( const char *pfmt, ... ); +void LogMsgNoCr ( const char *pfmt, ... ); + +int SetErrorNumber(int errno); +int GetErrorNumber(); +int* GetErrorNumberPointer(); + +int sprintf(char* buf, const char* fmt, ...); + +void OnAssertionFail(const char *cond_msg, const char *file, int line); +#define assert(cond) do { if (!(cond)) OnAssertionFail(#cond, __FILE__, __LINE__); } while (0) +#define ASSERT assert + +#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) + +#endif//_CRTLIB_H \ No newline at end of file diff --git a/crt2/src/entry.c b/crt2/src/entry.c new file mode 100644 index 00000000..f09fa960 --- /dev/null +++ b/crt2/src/entry.c @@ -0,0 +1,62 @@ +// *************************************************************** +// entry.c - Creation date: 21/04/2022 +// ------------------------------------------------------------- +// NanoShell C Runtime Library +// Copyright (C) 2022 iProgramInCpp - Licensed under GPL V3 +// +// *************************************************************** +// Programmer(s): iProgramInCpp (iprogramincpp@gmail.com) +// *************************************************************** + +#include "crtlib.h" +#include "crtinternal.h" + +__attribute__((noreturn)) void exit (int number); + +int main(int argc, char** argv); +void MemMgrInitializeMemory(); + +void _I_Setup(); + +__attribute__((noreturn)) +void _CEntry(const char* arg) +{ + MemMgrInitializeMemory(); + + _I_Setup(); + + char* argv[128]; + argv[0] = NULL; + + int returnValue; + + // No arguments? Just call with argc of 0 + if (*arg == 0) + { + returnValue = main(0, argv); + exit (returnValue); + } + + // Have arguments. Copy argument string + int slen = strlen (arg); + char string [slen + 1]; + strcpy (string, arg); + + // Tokenize passed in argument string. + int argc = 0; + + TokenState pState; + memset (&pState, 0, sizeof pState); + + argv[argc++] = Tokenize (&pState, string, " "); + while ((argv[argc++] = Tokenize (&pState, NULL, " "))); + if (argv[argc - 1] == NULL) + argc--; + + // Finally call the real entry point + returnValue = main(argc, argv); + exit(returnValue); + + // no return. +} + diff --git a/crt2/src/math.asm b/crt2/src/math.asm new file mode 100644 index 00000000..100e41c8 --- /dev/null +++ b/crt2/src/math.asm @@ -0,0 +1 @@ +; Everything in this file is now linked from libgcc. \ No newline at end of file diff --git a/crt2/src/zstub.c b/crt2/src/zstub.c new file mode 100644 index 00000000..22503483 --- /dev/null +++ b/crt2/src/zstub.c @@ -0,0 +1,2 @@ +// This is a stub. +// crti.o and crtn.o are both unused, crt1.o is the one that actually contains runtime code. \ No newline at end of file diff --git a/fs/Bin/List.nse b/fs/Bin/List.nse index 879d6a4af358635c1f8ab66b51d7bc792cc49412..ab0bcf85024bfa9e576e0fe7a7df06e0a9e46d80 100644 GIT binary patch literal 12312 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 diff --git a/src/elf.c b/src/elf.c index 890873f3..0adab526 100644 --- a/src/elf.c +++ b/src/elf.c @@ -203,54 +203,19 @@ ElfSymbol* ExLookUpSymbol(Process* pProc, uintptr_t address) return ElfGetSymbolAtAddress(&lb, address); } -bool ElfSymbolLessThan(ElfSymbol* p1, ElfSymbol* p2) +int ElfSymbolCompare(void* p1v, void* p2v, UNUSED void* ctx) { - if (p1->m_stValue != p2->m_stValue) return p1->m_stValue < p2->m_stValue; - if (p1->m_stSize != p2->m_stSize) return p1->m_stSize < p2->m_stSize; - if (p1->m_stInfo != p2->m_stInfo) return p1->m_stInfo < p2->m_stInfo; - if (p1->m_stName != p2->m_stName) return p1->m_stName < p2->m_stName; - return false; -} - -// merge sort implementation -void ElfSortSymbolsSub(ElfSymbol* pSymbols, ElfSymbol* pTempStorage, int left, int right) -{ - // do we have a trivial problem? - if (left >= right) return; - - int mid = (left + right) / 2; - - // sort the first half - ElfSortSymbolsSub(pSymbols, pTempStorage, left, mid); - - // sort the second half - ElfSortSymbolsSub(pSymbols, pTempStorage, mid + 1, right); - - // interleave the halves - int indexL = left, indexR = mid + 1, indexI = 0; - while (indexL <= mid && indexR <= right) - { - if (ElfSymbolLessThan(&pSymbols[indexL], &pSymbols[indexR])) - pTempStorage[indexI++] = pSymbols[indexL++]; - else - pTempStorage[indexI++] = pSymbols[indexR++]; - } - - // append the other parts - while (indexL <= mid) pTempStorage[indexI++] = pSymbols[indexL++]; - while (indexR <= right) pTempStorage[indexI++] = pSymbols[indexR++]; - - // copy the elements back into the array - memcpy(&pSymbols[left], pTempStorage, (right - left + 1) * sizeof (ElfSymbol)); + ElfSymbol *p1 = p1v, *p2 = p2v; + if (p1->m_stValue != p2->m_stValue) return p1->m_stValue - p2->m_stValue; + if (p1->m_stSize != p2->m_stSize) return p1->m_stSize - p2->m_stSize; + if (p1->m_stInfo != p2->m_stInfo) return p1->m_stInfo - p2->m_stInfo; + if (p1->m_stName != p2->m_stName) return p1->m_stName - p2->m_stName; + return 0; } void ElfSortSymbols(ElfSymbol* pSymbols, int nEntries) { - ElfSymbol* pIntermediateStorage = MmAllocate(nEntries * sizeof (ElfSymbol)); - - ElfSortSymbolsSub(pSymbols, pIntermediateStorage, 0, nEntries - 1); - - MmFree(pIntermediateStorage); + HeapSort(pSymbols, sizeof(ElfSymbol), nEntries, ElfSymbolCompare, NULL); } void ElfSetupSymTabEntries(ElfSymbol** pSymbolsPtr, const char* pStrTab, int* pnEntries) From 7073350837969d583585e8c64c532102f0733e32 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 7 Jun 2023 14:48:58 +0300 Subject: [PATCH 2/3] * Small reformat in elf.c --- src/elf.c | 279 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 159 insertions(+), 120 deletions(-) diff --git a/src/elf.c b/src/elf.c index 0adab526..f0e45e36 100644 --- a/src/elf.c +++ b/src/elf.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -96,16 +97,20 @@ void ElfCleanup (UNUSED ElfProcess* pProcess) { } -void ElfMapAddress(ElfProcess* pProc, void *virt, size_t size, void* data, size_t fileSize) +bool ElfMapAddress(ElfProcess* pProc, void *virt, size_t size, void* data, size_t fileSize) { uintptr_t virtHint = (uintptr_t)virt & ~(PAGE_SIZE - 1), virtOffset = (uintptr_t)virt & (PAGE_SIZE - 1); size_t sizePages = (((size + virtOffset - 1) >> 12) + 1); - MuMapMemoryFixedHint(pProc->m_heap, virtHint, sizePages, NULL, true, CLOBBER_SKIP, false, PAGE_BIT_SCRUB_ZERO); + if (!MuMapMemoryFixedHint(pProc->m_heap, virtHint, sizePages, NULL, true, CLOBBER_SKIP, false, PAGE_BIT_SCRUB_ZERO)) + { + return false; + } //memset((void*)virtHint, 0, sizePages * PAGE_SIZE); memcpy(virt, data, fileSize); + return true; } void ElfDumpInfo(ElfHeader* pHeader) @@ -203,19 +208,54 @@ ElfSymbol* ExLookUpSymbol(Process* pProc, uintptr_t address) return ElfGetSymbolAtAddress(&lb, address); } -int ElfSymbolCompare(void* p1v, void* p2v, UNUSED void* ctx) +bool ElfSymbolLessThan(ElfSymbol* p1, ElfSymbol* p2) { - ElfSymbol *p1 = p1v, *p2 = p2v; - if (p1->m_stValue != p2->m_stValue) return p1->m_stValue - p2->m_stValue; - if (p1->m_stSize != p2->m_stSize) return p1->m_stSize - p2->m_stSize; - if (p1->m_stInfo != p2->m_stInfo) return p1->m_stInfo - p2->m_stInfo; - if (p1->m_stName != p2->m_stName) return p1->m_stName - p2->m_stName; - return 0; + if (p1->m_stValue != p2->m_stValue) return p1->m_stValue < p2->m_stValue; + if (p1->m_stSize != p2->m_stSize) return p1->m_stSize < p2->m_stSize; + if (p1->m_stInfo != p2->m_stInfo) return p1->m_stInfo < p2->m_stInfo; + if (p1->m_stName != p2->m_stName) return p1->m_stName < p2->m_stName; + return false; +} + +// merge sort implementation +void ElfSortSymbolsSub(ElfSymbol* pSymbols, ElfSymbol* pTempStorage, int left, int right) +{ + // do we have a trivial problem? + if (left >= right) return; + + int mid = (left + right) / 2; + + // sort the first half + ElfSortSymbolsSub(pSymbols, pTempStorage, left, mid); + + // sort the second half + ElfSortSymbolsSub(pSymbols, pTempStorage, mid + 1, right); + + // interleave the halves + int indexL = left, indexR = mid + 1, indexI = 0; + while (indexL <= mid && indexR <= right) + { + if (ElfSymbolLessThan(&pSymbols[indexL], &pSymbols[indexR])) + pTempStorage[indexI++] = pSymbols[indexL++]; + else + pTempStorage[indexI++] = pSymbols[indexR++]; + } + + // append the other parts + while (indexL <= mid) pTempStorage[indexI++] = pSymbols[indexL++]; + while (indexR <= right) pTempStorage[indexI++] = pSymbols[indexR++]; + + // copy the elements back into the array + memcpy(&pSymbols[left], pTempStorage, (right - left + 1) * sizeof (ElfSymbol)); } void ElfSortSymbols(ElfSymbol* pSymbols, int nEntries) { - HeapSort(pSymbols, sizeof(ElfSymbol), nEntries, ElfSymbolCompare, NULL); + ElfSymbol* pIntermediateStorage = MmAllocate(nEntries * sizeof (ElfSymbol)); + + ElfSortSymbolsSub(pSymbols, pIntermediateStorage, 0, nEntries - 1); + + MmFree(pIntermediateStorage); } void ElfSetupSymTabEntries(ElfSymbol** pSymbolsPtr, const char* pStrTab, int* pnEntries) @@ -314,139 +354,138 @@ static int ElfExecute (void *pElfFile, UNUSED size_t size, const char* pArgs, in } else { - ElfMapAddress (&proc, addr, size1, &pElfData[offs], size2); + if (!ElfMapAddress(&proc, addr, size1, &pElfData[offs], size2)) + { + failed = true; + break; + } } } if (failed) return ELF_INVALID_SEGMENTS; + + EDLogMsg("(loaded and mapped everything, activating heap!)"); + + int strTabShLink = -1, symTabIndex = -1; + + for (int i = 0; i < pHeader->m_shNum; i++) { - EDLogMsg("(loaded and mapped everything, activating heap!)"); - MuUseHeap (pHeap); - - int strTabShLink = -1, symTabIndex = -1; + ElfSectHeader* pSectHeader = (ElfSectHeader*)(pElfData + pHeader->m_shOffs + i * pHeader->m_shEntSize); - for (int i = 0; i < pHeader->m_shNum; i++) + if (pSectHeader->m_type == SHT_SYMTAB) + { + strTabShLink = pSectHeader->m_shLink; + symTabIndex = i; + break; + } + } + + ElfSectHeader* pShStrTabHdr = (ElfSectHeader*)(pElfData + pHeader->m_shOffs + pHeader->m_shStrNdx * pHeader->m_shEntSize); + + for (int i = 0; i < pHeader->m_shNum; i++) + { + ElfSectHeader* pSectHeader = (ElfSectHeader*)(pElfData + pHeader->m_shOffs + i * pHeader->m_shEntSize); + if (pSectHeader->m_type == SHT_NOBITS) { - ElfSectHeader* pSectHeader = (ElfSectHeader*)(pElfData + pHeader->m_shOffs + i * pHeader->m_shEntSize); - if (pSectHeader->m_type == SHT_SYMTAB) - { - strTabShLink = pSectHeader->m_shLink; - symTabIndex = i; - break; - } } - - ElfSectHeader* pShStrTabHdr = (ElfSectHeader*)(pElfData + pHeader->m_shOffs + pHeader->m_shStrNdx * pHeader->m_shEntSize); - - for (int i = 0; i < pHeader->m_shNum; i++) + if (pSectHeader->m_type == SHT_PROGBITS) { - ElfSectHeader* pSectHeader = (ElfSectHeader*)(pElfData + pHeader->m_shOffs + i * pHeader->m_shEntSize); - if (pSectHeader->m_type == SHT_NOBITS) - { - - } - if (pSectHeader->m_type == SHT_PROGBITS) - { - // check the header's name - const char* pName = (const char*)(pElfData + pShStrTabHdr->m_offset + pSectHeader->m_name); - - if (strcmp(pName, ".nanoshell") == 0) - ExSetProgramInfo((ProgramInfo*)pSectHeader->m_addr); - else if (strcmp(pName, ".nanoshell_resources") == 0) - if (!ExLoadResourceTable((void*)pSectHeader->m_addr)) - { - failed = true; - break; - } - } - if (pSectHeader->m_type == SHT_SYMTAB) + // check the header's name + const char* pName = (const char*)(pElfData + pShStrTabHdr->m_offset + pSectHeader->m_name); + + if (strcmp(pName, ".nanoshell") == 0) { - SLogMsg("Found SymTab. m_offset: %x Size: %x", pSectHeader->m_offset, pSectHeader->m_shSize); - - uint8_t* pTableStart = &pElfData[pSectHeader->m_offset]; - size_t nTableSize = pSectHeader->m_shSize; - - // allocate the symbol table - void *pTableMem = MmAllocate(nTableSize); - - // copy the contents from the ELF data - memcpy(pTableMem, pTableStart, nTableSize); - - // set the loader block's relevant fields - pLoaderBlock->pSymTab = pTableMem; - pLoaderBlock->nSymTabEntries = nTableSize / sizeof(ElfSymbol); - - // have we loaded the strtab? - if (pLoaderBlock->pStrTab && !pLoaderBlock->bSetUpSymTab) - { - ElfSetupSymTabEntries((ElfSymbol**)&pLoaderBlock->pSymTab, (const char*)pLoaderBlock->pStrTab, &pLoaderBlock->nSymTabEntries); - pLoaderBlock->bSetUpSymTab = true; - } + ExSetProgramInfo((ProgramInfo*)pSectHeader->m_addr); } - if (pSectHeader->m_type == SHT_STRTAB && i == strTabShLink) + else if (strcmp(pName, ".nanoshell_resources") == 0) { - SLogMsg("Found StrTab. m_offset: %x Size: %x", pSectHeader->m_offset, pSectHeader->m_shSize); - - uint8_t* pTableStart = &pElfData[pSectHeader->m_offset]; - size_t nTableSize = pSectHeader->m_shSize; - - // allocate the symbol table - void *pTableMem = MmAllocate(nTableSize); - - // copy the contents from the ELF data - memcpy(pTableMem, pTableStart, nTableSize); - - // set the loader block's relevant fields - pLoaderBlock->pStrTab = pTableMem; - - // have we loaded the symtab? - if (pLoaderBlock->pSymTab && !pLoaderBlock->bSetUpSymTab) + if (!ExLoadResourceTable((void*)pSectHeader->m_addr)) { - ElfSetupSymTabEntries((ElfSymbol**)&pLoaderBlock->pSymTab, (const char*)pLoaderBlock->pStrTab, &pLoaderBlock->nSymTabEntries); - pLoaderBlock->bSetUpSymTab = true; + failed = true; + break; } } } - - // now, copy the symtab and strtab data into the process' structure - Process* pThisProcess = ExGetRunningProc(); - pThisProcess->pSymTab = pLoaderBlock->pSymTab; - pThisProcess->pStrTab = pLoaderBlock->pStrTab; - pThisProcess->nSymTabEntries = pLoaderBlock->nSymTabEntries; - - EDLogMsg("The ELF setup is done, jumping to the entry! Wish us luck!!!"); - - // test out the resource stuff - Resource* pResource = ExLookUpResource(10000); - - if (!pResource) + if (pSectHeader->m_type == SHT_SYMTAB) { - SLogMsg("Resource 10000 not found."); + SLogMsg("Found SymTab. m_offset: %x Size: %x", pSectHeader->m_offset, pSectHeader->m_shSize); + + uint8_t* pTableStart = &pElfData[pSectHeader->m_offset]; + size_t nTableSize = pSectHeader->m_shSize; + + // allocate the symbol table + void *pTableMem = MmAllocate(nTableSize); + + // copy the contents from the ELF data + memcpy(pTableMem, pTableStart, nTableSize); + + // set the loader block's relevant fields + pLoaderBlock->pSymTab = pTableMem; + pLoaderBlock->nSymTabEntries = nTableSize / sizeof(ElfSymbol); + + // have we loaded the strtab? + if (pLoaderBlock->pStrTab && !pLoaderBlock->bSetUpSymTab) + { + ElfSetupSymTabEntries((ElfSymbol**)&pLoaderBlock->pSymTab, (const char*)pLoaderBlock->pStrTab, &pLoaderBlock->nSymTabEntries); + pLoaderBlock->bSetUpSymTab = true; + } } - else + if (pSectHeader->m_type == SHT_STRTAB && i == strTabShLink) { - SLogMsg("Resource 10000 Found Type %d", pResource->m_type); + SLogMsg("Found StrTab. m_offset: %x Size: %x", pSectHeader->m_offset, pSectHeader->m_shSize); + + uint8_t* pTableStart = &pElfData[pSectHeader->m_offset]; + size_t nTableSize = pSectHeader->m_shSize; + + // allocate the symbol table + void *pTableMem = MmAllocate(nTableSize); + + // copy the contents from the ELF data + memcpy(pTableMem, pTableStart, nTableSize); + + // set the loader block's relevant fields + pLoaderBlock->pStrTab = pTableMem; + + // have we loaded the symtab? + if (pLoaderBlock->pSymTab && !pLoaderBlock->bSetUpSymTab) + { + ElfSetupSymTabEntries((ElfSymbol**)&pLoaderBlock->pSymTab, (const char*)pLoaderBlock->pStrTab, &pLoaderBlock->nSymTabEntries); + pLoaderBlock->bSetUpSymTab = true; + } } - - //now that we have switched, call the entry func: - ElfEntry entry = (ElfEntry)pHeader->m_entry; - - // this is a bit complex, but I'll break it down. This does a couple things: - // - push `pArgs` as the only argument (%1) - // - call `entry` (%2) - // - restore esp to normal - // - mark ebx, esi, and edi as being clobbered - // - the return value is in %0 (eax) - int returnValue = 0; - asm("pushl %1\ncall *%2\nadd $4, %%esp" : "=a"(returnValue) : "r"(pArgs), "r"(entry) : "ebx", "esi", "edi"); - - EDLogMsg("Executable has exited."); - - *pErrCodeOut = g_lastReturnCode = returnValue; } - return failed ? ELF_INVALID_SEGMENTS : ELF_ERROR_NONE; + if (failed) + { + MmFree(pLoaderBlock->pSymTab); + MmFree(pLoaderBlock->pStrTab); + return ELF_INVALID_SEGMENTS; + } + + // now, copy the symtab and strtab data into the process' structure + Process* pThisProcess = ExGetRunningProc(); + pThisProcess->pSymTab = pLoaderBlock->pSymTab; + pThisProcess->pStrTab = pLoaderBlock->pStrTab; + pThisProcess->nSymTabEntries = pLoaderBlock->nSymTabEntries; + + //now that we have switched, call the entry func: + ElfEntry entry = (ElfEntry)pHeader->m_entry; + + // this is a bit complex, but I'll break it down. This does a couple things: + // - push `pArgs` as the only argument (%1) + // - call `entry` (%2) + // - restore esp to normal + // - mark ebx, esi, and edi as being clobbered + // - the return value is in %0 (eax) + int returnValue = 0; + asm("pushl %1\ncall *%2\nadd $4, %%esp" : "=a"(returnValue) : "r"(pArgs), "r"(entry) : "ebx", "esi", "edi"); + + EDLogMsg("Executable has exited."); + + *pErrCodeOut = g_lastReturnCode = returnValue; + + return ELF_ERROR_NONE; } const char *gElfErrorCodes[] = From ff955efecbbc9c56b4309a728dd40d5ee8468166 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 7 Jun 2023 20:09:26 +0300 Subject: [PATCH 3/3] * Incomplete work. --- fs/libnanoshell.so | Bin 0 -> 191228 bytes include/elf.h | 54 ++++++++++++++++++ src/elf.c | 139 ++++++++++++++++++++++++++++++--------------- 3 files changed, 148 insertions(+), 45 deletions(-) create mode 100644 fs/libnanoshell.so diff --git a/fs/libnanoshell.so b/fs/libnanoshell.so new file mode 100644 index 0000000000000000000000000000000000000000..ed56b479cf5bcdf7808ecb319e8529c525559970 GIT binary patch literal 191228 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 literal 0 HcmV?d00001 diff --git a/include/elf.h b/include/elf.h index 730e50a7..2cbbada5 100644 --- a/include/elf.h +++ b/include/elf.h @@ -11,6 +11,8 @@ #include #include +#define C_MAX_ELF_LIBRARIES (32) + #define SAFE_FREE(ptr) do { \ if (ptr) { \ MmFree(ptr); \ @@ -112,6 +114,8 @@ enum { ELF_TYPE_NONE = 0, //Unknown ELF_TYPE_RELOC, //Relocatable file ELF_TYPE_EXEC, //Executable file + ELF_TYPE_DYN, //Shared object + ELF_TYPE_CORE, //Core dump file }; #define ELF_MACH_386 3 //x86 Machine type @@ -136,6 +140,10 @@ enum { ELF_EXEC_BLOCKED, ELF_EXEC_BLOCKED_GLOBALLY, ELF_RESOURCE_TABLE_NOT_ORDERED, + ELF_LIB_TOO_MANY_LIBRARIES, + ELF_LIB_INVALID_PROGHDRS, + ELF_LIB_BAD_RELOC_ENTRY, + ELF_LIB_OUT_OF_MEMORY, ELF_ERR_COUNT, }; @@ -156,6 +164,52 @@ enum //... }; +typedef struct +{ + UserHeap* m_heap; +} +ElfProcess; + +typedef struct +{ + // If the shared library is used. + bool m_bUsed; + + // The pointer to the shared object. To unload it, simply MmFree() it. + void* m_pPtr; + + // Amount of users of the object. If it's 0, it can be unloaded at any time. + // A user is an object file that hooked into the library. + int m_nUsers; + + // The symbol table of the shared object. + void* m_pSymTab; + + // The string table of the shared object. + void* m_pStrTab; +} +ElfSharedObject; + +typedef struct +{ + void* pFileData; + size_t nFileSize; + bool bGui; //false if ran from command shell + bool bAsync; //false if the parent is waiting for this to finish + bool bExecDone; //true if the execution of this process has completed + int nHeapSize; //can be dictated by user settings later, for now it should be 512 + char sArgs[1024]; + char sFileName[PATH_MAX+5]; + int nElfErrorCode; // The error code that the ELF executive returns if the ELF file didn't run + int nElfErrorCodeExec; // The error code that the ELF file itself returns. + void* pSymTab; + void* pStrTab; + bool bSetUpSymTab; + int nSymTabEntries; + uint64_t nParentTaskRID; +} +ElfLoaderBlock; + const char *ElfGetErrorMsg (int error_code); int ElfRunProgram(const char *pFileName, const char *args, bool bAsync, bool bGui, UNUSED int nHeapSize, int *pElfErrorCodeOut); ElfSymbol* ExLookUpSymbol(Process* pProc, uintptr_t address); diff --git a/src/elf.c b/src/elf.c index f0e45e36..72af4d75 100644 --- a/src/elf.c +++ b/src/elf.c @@ -14,7 +14,7 @@ #include #include "mm/memoryi.h" // The ELF loader has legitimate reason to use memory manager's internal stuff. -//#define ELF_DEBUG +#define ELF_DEBUG //#define ELFSYM_DEBUG #ifdef ELF_DEBUG @@ -23,11 +23,58 @@ #define EDLogMsg(...) #endif -typedef struct +SafeLock g_ElfSharedObjectLock; +ElfSharedObject g_ElfSharedObjects[C_MAX_ELF_LIBRARIES]; + +ElfSharedObject* ElfCreateEmptySharedObject() +{ + LockAcquire(&g_ElfSharedObjectLock); + + ElfSharedObject* pObj = NULL; + for (int i = 0; i < C_MAX_ELF_LIBRARIES; i++) + { + if (!g_ElfSharedObjects[i].m_bUsed) + { + pObj = &g_ElfSharedObjects[i]; + pObj->m_bUsed = true; + break; + } + } + + LockFree(&g_ElfSharedObjectLock); + return pObj; +} + +bool ElfTryFreeSharedObject(ElfSharedObject* pSO) { - UserHeap* m_heap; + bool bInterruptsWereCleared = false; + if (!KeCheckInterruptsDisabled()) + { + bInterruptsWereCleared = true; + // clear interrupts to ensure that no more users are coming: + cli; + } + + // If there are still people using this library, bail + if (pSO->m_nUsers) + goto EndFailure; + + pSO->m_bUsed = false; + + // We keep the interrupts cleared to free these: + MmFreeID(pSO->m_pPtr); + MmFreeID(pSO->m_pSymTab); + MmFreeID(pSO->m_pStrTab); + + if (bInterruptsWereCleared) + sti; + return true; + +EndFailure: + if (bInterruptsWereCleared) + sti; + return false; } -ElfProcess; const char* ElfGetArchitectureString(uint16_t machine, uint8_t word_size) { @@ -122,26 +169,6 @@ void ElfDumpInfo(ElfHeader* pHeader) extern int g_lastReturnCode; -typedef struct -{ - void* pFileData; - size_t nFileSize; - bool bGui; //false if ran from command shell - bool bAsync; //false if the parent is waiting for this to finish - bool bExecDone; //true if the execution of this process has completed - int nHeapSize; //can be dictated by user settings later, for now it should be 512 - char sArgs[1024]; - char sFileName[PATH_MAX+5]; - int nElfErrorCode; // The error code that the ELF executive returns if the ELF file didn't run - int nElfErrorCodeExec; // The error code that the ELF file itself returns. - void* pSymTab; - void* pStrTab; - bool bSetUpSymTab; - int nSymTabEntries; - uint64_t nParentTaskRID; -} -ElfLoaderBlock; - // TODO: Maybe update this to outside of the elf bubble? It could work for other executable files too ElfSymbol* ElfGetSymbolAtIndex(ElfLoaderBlock* pLBlock, int index) @@ -305,31 +332,17 @@ void ElfSetupSymTabEntries(ElfSymbol** pSymbolsPtr, const char* pStrTab, int* pn *pSymbolsPtr = pNewSymbols; } -static int ElfExecute (void *pElfFile, UNUSED size_t size, const char* pArgs, int *pErrCodeOut, ElfLoaderBlock* pLoaderBlock) +// Hint is for relocatables +static int ElfLoadProgram(ElfProcess* pProcess, ElfHeader* pHeader, ElfLoaderBlock* pLoaderBlock) { - EDLogMsg("Loading elf file"); - - // The heap associated with this process - UserHeap *pHeap = MuGetCurrentHeap(); - - ElfProcess proc; - memset(&proc, 0, sizeof(proc)); - - proc.m_heap = pHeap; - + void* pElfFile = pHeader; uint8_t* pElfData = (uint8_t*)pElfFile; //to do arithmetic with this - //check the header. - ElfHeader* pHeader = (ElfHeader*)pElfFile; - - int errCode = ElfIsSupported(pHeader); - if (errCode != 1) //not supported. - { - LogMsg("Got error %d while loading the elf.", errCode); - return errCode; - } bool failed = false; + uint32_t relocationOffset = 0, maxSize = 0; + // Navigate the program headers. If one + EDLogMsg("(loading prog hdrs into memory...)"); for (int i = 0; i < pHeader->m_phNum; i++) { @@ -354,7 +367,7 @@ static int ElfExecute (void *pElfFile, UNUSED size_t size, const char* pArgs, in } else { - if (!ElfMapAddress(&proc, addr, size1, &pElfData[offs], size2)) + if (!ElfMapAddress(pProcess, addr, size1, &pElfData[offs], size2)) { failed = true; break; @@ -463,6 +476,37 @@ static int ElfExecute (void *pElfFile, UNUSED size_t size, const char* pArgs, in return ELF_INVALID_SEGMENTS; } + return ELF_ERROR_NONE; +} + +static int ElfExecute (void *pElfFile, UNUSED size_t size, const char* pArgs, int *pErrCodeOut, ElfLoaderBlock* pLoaderBlock) +{ + EDLogMsg("Loading elf file"); + + // The heap associated with this process + UserHeap *pHeap = MuGetCurrentHeap(); + + ElfProcess proc; + memset(&proc, 0, sizeof(proc)); + + proc.m_heap = pHeap; + + uint8_t* pElfData = (uint8_t*)pElfFile; //to do arithmetic with this + //check the header. + ElfHeader* pHeader = (ElfHeader*)pElfFile; + + int errCode = ElfIsSupported(pHeader); + if (errCode != 1) //not supported. + { + LogMsg("Got error %d while loading the elf.", errCode); + return errCode; + } + + // load the program into memory: + int result = ElfLoadProgram(&proc, pHeader, pLoaderBlock); + if (result != ELF_ERROR_NONE) + return result; + // now, copy the symtab and strtab data into the process' structure Process* pThisProcess = ExGetRunningProc(); pThisProcess->pSymTab = pLoaderBlock->pSymTab; @@ -507,6 +551,11 @@ const char *gElfErrorCodes[] = "The execution of this program was terminated.", "The execution of this file is not permitted at the moment.", "The execution of program files is not permitted. Please contact your system administrator.", + "The resource table of the image file %s is not ordered." + "The shared library %s could not be loaded because there are too many libraries loaded.", + "The shared library %s could not be loaded because of an invalid program segment layout.", + "The shared library %s could not be loaded because of a corrupted relocation entry.", + "The shared library %s could not be loaded because the system has run out of memory.", }; const char *ElfGetErrorMsg (int error_code)