diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5dc90b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.exe +*.ilk +*.obj +*.pdb \ No newline at end of file diff --git a/bastd.c b/bastd.c index 138ad14..a4de165 100644 --- a/bastd.c +++ b/bastd.c @@ -1,6 +1,160 @@ #ifndef BASTD_C #define BASTD_C +//////////////////////////////// +// NOTE(allen): Context Cracking + +// NOTE(allen): Untangle Compiler, OS & Architecture +#if defined(__clang__) +# define COMPILER_CLANG 1 + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# elif defined(__gnu_linux__) +# define OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) +# define OS_MAC 1 +# else +# error missing OS detection +# endif + +# if defined(__amd64__) +# define ARCH_X64 1 +// TODO(allen): verify this works on clang +# elif defined(__i386__) +# define ARCH_X86 1 +// TODO(allen): verify this works on clang +# elif defined(__arm__) +# define ARCH_ARM 1 +// TODO(allen): verify this works on clang +# elif defined(__aarch64__) +# define ARCH_ARM64 1 +# else +# error missing ARCH detection +# endif + +#elif defined(_MSC_VER) +# define COMPILER_CL 1 + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# else +# error missing OS detection +# endif + +# if defined(_M_AMD64) +# define ARCH_X64 1 +# elif defined(_M_I86) +# define ARCH_X86 1 +# elif defined(_M_ARM) +# define ARCH_ARM 1 +// TODO(allen): ARM64? +# else +# error missing ARCH detection +# endif + +#elif defined(__GNUC__) +# define COMPILER_GCC 1 + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# elif defined(__gnu_linux__) +# define OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) +# define OS_MAC 1 +# else +# error missing OS detection +# endif + +# if defined(__amd64__) +# define ARCH_X64 1 +# elif defined(__i386__) +# define ARCH_X86 1 +# elif defined(__arm__) +# define ARCH_ARM 1 +# elif defined(__aarch64__) +# define ARCH_ARM64 1 +# else +# error missing ARCH detection +# endif + +#else +# error no context cracking for this compiler +#endif + +#if !defined(COMPILER_CL) +# define COMPILER_CL 0 +#endif +#if !defined(COMPILER_CLANG) +# define COMPILER_CLANG 0 +#endif +#if !defined(COMPILER_GCC) +# define COMPILER_GCC 0 +#endif +#if !defined(OS_WINDOWS) +# define OS_WINDOWS 0 +#endif +#if !defined(OS_LINUX) +# define OS_LINUX 0 +#endif +#if !defined(OS_MAC) +# define OS_MAC 0 +#endif +#if !defined(ARCH_X64) +# define ARCH_X64 0 +#endif +#if !defined(ARCH_X86) +# define ARCH_X86 0 +#endif +#if !defined(ARCH_ARM) +# define ARCH_ARM 0 +#endif +#if !defined(ARCH_ARM64) +# define ARCH_ARM64 0 +#endif + + +// NOTE(allen): Language +#if defined(__cplusplus) +# define LANG_CXX 1 +#else +# define LANG_C 1 +#endif + +#if !defined(LANG_CXX) +# define LANG_CXX 0 +#endif +#if !defined(LANG_C) +# define LANG_C 0 +#endif + + +// NOTE(allen): Profiler +#if !defined(PROFILER_SPALL) +# define PROFILER_SPALL 0 +#endif + + +// NOTE(allen): Determine Intrinsics Mode +#if OS_WINDOWS +# if COMPILER_CL || COMPILER_CLANG +# define INTRINSICS_MICROSOFT 1 +# endif +#endif + +#if !defined(INTRINSICS_MICROSOFT) +# define INTRINSICS_MICROSOFT 0 +#endif + + +// NOTE(allen): Setup Pointer Size Macro +#if ARCH_X64 || ARCH_ARM64 +# define ARCH_ADDRSIZE 64 +#else +# define ARCH_ADDRSIZE 32 +#endif + +// Types #include typedef int8_t I8; @@ -15,17 +169,34 @@ typedef uint64_t U64; typedef float F32; typedef double F64; +typedef uintptr_t UPtr; +typedef ptrdiff_t ISize; +typedef size_t USize; + typedef U32 B32; #define TRUE 1 #define FALSE 0 +// Macros #define FUNCTION static #define GLOBAL_VAR static #define LOCAL_PERSIST static #define STMNT(s) do{ s }while(0) -// TODO: Add Linux Version -#define ASSERT(x, msg) STMNT(if (!x) __debugbreak();) +#define ASSERT(x, msg) STMNT(if (!x) OS_DEBUGBREAK();) + +#define CONCAT_(a,b) a##b +#define CONCAT(a,b) CONCAT_(a,b) + +#define NIL 0 + +#define COUNT_OF(a) (ISize)(sizeof(a) / sizeof(*(a))) +#define LENGTH_OF(s) (COUNT_OF(s) - 1) + +// Includes +#include "bastd/string.c" +#include "bastd/os_windows.c" +#include "bastd/mem.c" #endif //BASTD_C \ No newline at end of file diff --git a/bastd/mem.c b/bastd/mem.c new file mode 100644 index 0000000..b0733fc --- /dev/null +++ b/bastd/mem.c @@ -0,0 +1,116 @@ +#ifndef BASTD_MEM_C +#define BASTD_MEM_C + +FUNCTION void * +M_memorySet(void *buffer, U8 value, ISize length) +{ + U8* p = buffer; + while (length-- > 0) { + *p++ = value; + } + return buffer; +} + +FUNCTION void * +M_memoryCopy(void *dst, void *src, size_t n) +{ + U8 *s = (U8 *)src; + U8 *d = (U8 *)dst; + + for (I32 i = 0; i < n; i++) { + d[i] = s[i]; + } + + return dst; +} + +/* Alloc function taken from Lua. + If ptr == NIL, old_size irrelevant + If ptr != NIL, old_size = size of block of memory at ptr + If new_size > 0, function like realloc and either resize or create + If new_size <= 0, function like free and clear the memory +*/ +typedef void *(*M_AllocFunc)(void *ctx, void *ptr, ISize old_size, ISize new_size); + +typedef struct M_Allocator M_Allocator; +struct M_Allocator { + M_AllocFunc alloc; + void* ctx; +}; + +#define M_MAKE(T, n, a) ((T *)((a).alloc((a).ctx, NIL, 0, sizeof(T) * n))) +#define M_RESIZE(p, o, n, a) ((a).alloc((a).ctx, p, sizeof(*(p)) * o, sizeof(*(p)) * n)) +#define M_RELEASE(p, s, a) ((a).alloc((a).ctx, p, sizeof(*(p)) * s, 0)) + +/* Linear Allocator (Arena) + Allocates variably-sized regions from a fixed-size block of memory. + A set of regions is freed by setting the allocator's offset to an earlier + value. +*/ +typedef struct M_Arena M_Arena; +struct M_Arena { + U8* beg; + U8* end; +}; + +#define DEFAULT_ALIGNMENT (2 * sizeof(void *)) + +FUNCTION void * +M_Arena_alloc(void *ctx, void *ptr, ISize old_size, ISize new_size) +{ + M_Arena *a = (M_Arena *)ctx; + + if (new_size <= 0) { + /* Arena can only free the most recent block. This allows to follow stack pattern. + What we do is move the end pointer forward by the size of the block + Then, because there is now more available space, the data will be + ignored following the next allocation. + */ + ISize padding = -old_size & (DEFAULT_ALIGNMENT - 1); + a->end += old_size + padding; + return NIL; + } else { + // Allocate a block + ASSERT(new_size > old_size, "Can't reallocate to a smaller block"); + + ISize padding = -(UPtr)a->beg & (DEFAULT_ALIGNMENT - 1); + ISize available = a->end - a->beg - padding; + + if (available < 0 || new_size > available) { + OS_Abort(S8("Out of Memory!")); + } + + void *p = NIL; + + if (a->beg == (U8 *)ptr + old_size) { + // This was the last allocated block, can extend allocation for realloc + p = ptr; + a->beg += padding + new_size; + } else { + // New allocation; + p = a->beg + padding; + a->beg += padding + new_size; + p = M_memorySet(p, 0, new_size); + + if (ptr != NIL) { + // Arbitrary block, copy data from old pointer + p = M_memoryCopy(p, ptr, old_size); + } + } + + return p; + } +} + +FUNCTION M_Arena +M_Arena_create(void* buffer, ISize capacity) +{ + M_Arena arena = {0}; + arena.beg = (U8 *)buffer; + arena.end = arena.beg ? arena.beg + capacity : 0; + return arena; +} + +#define M_ARENA_ALLOCATOR(a) (M_Allocator){M_Arena_alloc, (void *)&(a)} + +#endif//BASTD_MEM_C \ No newline at end of file diff --git a/bastd/os.c b/bastd/os.c new file mode 100644 index 0000000..1c8ac48 --- /dev/null +++ b/bastd/os.c @@ -0,0 +1,17 @@ +#ifndef BASTD_OS_C +#define BASTD_OS_C + +FUNCTION void OS_Abort(S8 msg); +FUNCTION void *OS_Alloc(ISize cap); + +#if defined(OS_WINDOWS) + +#include "os_windows.c" + +#elif defined(OS_LINUX) + +#include "os_linux.c" + +#endif//OS_WINDOWS + +#endif//BASTD_OS_C \ No newline at end of file diff --git a/bastd/os_windows.c b/bastd/os_windows.c new file mode 100644 index 0000000..530a56f --- /dev/null +++ b/bastd/os_windows.c @@ -0,0 +1,23 @@ +#ifndef BASTD_OS_WINDOWS_C +#define BASTD_OS_WINDOWS_C + +#include + +#define OS_DEBUGBREAK() __debugbreak(); + +FUNCTION void +OS_Abort(S8 msg) +{ + HANDLE stderr = GetStdHandle(-12); + U32 dummy; + WriteFile(stderr, msg.raw, msg.length, &dummy, 0); + ExitProcess(101); +} + +FUNCTION void * +OS_Alloc(ISize cap) +{ + return VirtualAlloc(NIL, cap, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +} + +#endif//BASTD_OS_WINDOWS_C \ No newline at end of file diff --git a/bastd/string.c b/bastd/string.c new file mode 100644 index 0000000..13ed6c7 --- /dev/null +++ b/bastd/string.c @@ -0,0 +1,12 @@ +#ifndef BASTD_STRING_C +#define BASTD_STRING_C + +typedef struct S8 S8; +struct S8 { + ISize length; + U8 *raw; +}; + +#define S8(s) (S8){(U8 *)s, LENGTH_OF(s)} + +#endif//BASTD_STRING_C \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..562dd5e --- /dev/null +++ b/build.bat @@ -0,0 +1,8 @@ +@echo off + +if not exist build\ mkdir build +pushd build + +cl /Zi ..\main.c + +popd \ No newline at end of file diff --git a/examples/memory.c b/examples/memory.c new file mode 100644 index 0000000..fd1f034 --- /dev/null +++ b/examples/memory.c @@ -0,0 +1,38 @@ +int +main(void) +{ + ISize buffer_size = 100 * sizeof(U8); + M_Arena arena = M_Arena_create(OS_Alloc(buffer_size), buffer_size); + M_Allocator permanent_allocator = M_ARENA_ALLOCATOR(arena); + + { + /* By cloning an arena at the start of the scope, all changes to the + arena are now local to the scope, so all allocations with be freed + at the end automatically. + */ + M_Arena scratch_arena = arena; + M_Allocator scratch_allocator = M_ARENA_ALLOCATOR(scratch_arena); + + U8* array = M_MAKE(U8, 20, scratch_allocator); + for (int i = 0; i < 20; i++) { + array[i] = i; + } + } // Auto clears here + + OS_DEBUGBREAK(); + + U8* array = M_MAKE(U8, 10, permanent_allocator); + for (int i = 10; i-- > 0;) { + array[10 - i] = i; + } + OS_DEBUGBREAK(); + + array = M_RESIZE(array, 10, 20, permanent_allocator); + for (int i = 20; i-- > 0;) { + array[20 - i] = i; + } + OS_DEBUGBREAK(); + + M_RELEASE(array, 20, permanent_allocator); + OS_DEBUGBREAK(); +} \ No newline at end of file diff --git a/main.c b/main.c index 5897346..01d2e87 100644 --- a/main.c +++ b/main.c @@ -5,16 +5,16 @@ Line */ // "GLOBAL_VAR" macro expands to "static" -GLOBAL_VAR F32 evil_number = 666 // variables names use snake_case +GLOBAL_VAR F32 evil_number = 666; // variables names use snake_case typedef struct RenderVertex RenderVertex; // typedefs come before structs struct RenderVertex { // struct names use PascalCase - F32 x, y, z -} + F32 x, y, z; +}; // Generic pattern. the output type will be "Slice_RenderVertex" -#define SLICE_TYPE RenderVertex -#include "slice.h" +// #define SLICE_TYPE RenderVertex +// #include "slice.h" // all functions prefixed with "FUNCTION" macro, which expands to "static" FUNCTION I8 // primitive types follow rust style @@ -29,32 +29,32 @@ addTwo(I8 a, I8 b) // Function names use camelCase defined as "static" , incase you decide to move it to a different compilation unit */ -CALLBACK Error -start(Slice_S8 args, Buffer stdin, Buffer stdout) -{ - /* all functions that belong to a type or a module are styled - "m_TypeName_functionName". "m" is a lower-case shortened version of the - module's name (usually 1-3 letters). - */ - Buffer_appendS8(&stdout, args[0]); - Buffer_flush(&stdout); +// CALLBACK Error +// start(Slice_S8 args, Buffer stdin, Buffer stdout) +// { +// /* all functions that belong to a type or a module are styled +// "m_TypeName_functionName". "m" is a lower-case shortened version of the +// module's name (usually 1-3 letters). +// */ +// Buffer_appendS8(&stdout, args[0]); +// Buffer_flush(&stdout); - r_Window window = r_Window_create(1280, 720, "Bastd Example"); - I32 frame_number = 0; +// r_Window window = r_Window_create(1280, 720, "Bastd Example"); +// I32 frame_number = 0; - for (!window.should_close) { - frame_number += 1; +// for (!window.should_close) { +// frame_number += 1; - // if there is no module, function is styled "TypeName_functionName" - Buffer_appendS8(&stdout, "Current frame: "); - Buffer_appendI32(&stdout, frame_number); - Buffer_appendChar(&stdout, '\n'); - Buffer_flush(&stdout); +// // if there is no module, function is styled "TypeName_functionName" +// Buffer_appendS8(&stdout, "Current frame: "); +// Buffer_appendI32(&stdout, frame_number); +// Buffer_appendChar(&stdout, '\n'); +// Buffer_flush(&stdout); - r_Window_tick(&window); - } +// r_Window_tick(&window); +// } - /* Enum names are styled with PascalCase. Enum values are styled - "TypeName_value_name"*/ - return Error_success; -} \ No newline at end of file +// /* Enum names are styled with PascalCase. Enum values are styled +// "TypeName_value_name"*/ +// return Error_success; +// } \ No newline at end of file