From e24186fc1917c5e8b91924af0c3c7c55816ff5d6 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Mon, 25 Jan 2021 22:01:00 +0100 Subject: Introducing MIC --- src/app.c | 44 +++++++++++++ src/app.h | 37 +++++++++++ src/main.c | 25 ++++++++ src/platform.h | 25 ++++++++ src/platform_posix.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/shorthand.h | 35 +++++++++++ 6 files changed, 337 insertions(+) create mode 100644 src/app.c create mode 100644 src/app.h create mode 100644 src/main.c create mode 100644 src/platform.h create mode 100644 src/platform_posix.c create mode 100644 src/shorthand.h (limited to 'src') diff --git a/src/app.c b/src/app.c new file mode 100644 index 0000000..a39bff0 --- /dev/null +++ b/src/app.c @@ -0,0 +1,44 @@ +#include "app.h" +#include "platform.h" + +static inline bool +app_init(AppState *state, PlatformAPI platform) { + platform.log("INIT"); + state->lt_memory = platform.calloc(LT_MEMORY_SIZE, sizeof(u8)); + state->st_memory = platform.calloc(ST_MEMORY_SIZE, sizeof(u8)); + return true; +} + +static inline void +app_destroy(AppState *state, PlatformAPI platform) { + (void)state; // Unused parameter. + platform.log("DESTROY"); +} + +static inline void +app_reload(AppState *state, PlatformAPI platform) { + (void)state; // Unused parameter. + platform.log("RELOAD"); +} + +static inline void +app_unload(AppState *state, PlatformAPI platform) { + (void)state; // Unused parameter. + platform.log("UNLOAD"); +} + +static inline bool +app_step(AppState *state, PlatformAPI platform) { + (void)state; // Unused parameter. + platform.log("STEP"); + platform.sleep(100000); + return true; +} + +const AppAPI APP_API = { + .init = app_init, + .destroy = app_destroy, + .reload = app_reload, + .step = app_step, + .unload = app_unload, +}; diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..8edbe58 --- /dev/null +++ b/src/app.h @@ -0,0 +1,37 @@ +#ifndef MIC_APP_H +#define MIC_APP_H + +#include "shorthand.h" +#include "platform.h" + +#define LT_MEMORY_SIZE GB(2) +#define ST_MEMORY_SIZE MB(100) + +typedef struct AppState { + // Long and short term memory. + char *lt_memory; + char *st_memory; +} AppState; + +// Function pointers for the AppAPI. +typedef struct AppAPI { + // Initialization code. Meant to be called exactly once before other API + // interactions. Returns the success or failure of the initialization + // process. + bool (*init)(AppState *state, PlatformAPI platform); + + // Resource deallocation and cleanup. Meant to be called exactly once at the + // end of the app. + void (*destroy)(AppState *state, PlatformAPI platform); + + // In case of hot code reloading, these functions handle the necessary + // resource setup and reloading. + void (*reload)(AppState *state, PlatformAPI platform); + void (*unload)(AppState *state, PlatformAPI platform); + + // Main update/step function for the app. Returns if the app should keep + // running. + bool (*step)(AppState *state, PlatformAPI platform); +} AppAPI; + +#endif // MIC_APP_H diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..52728ef --- /dev/null +++ b/src/main.c @@ -0,0 +1,25 @@ +#include "platform_posix.c" + +int +main(void) { + // App initialization. + AppAPI api = {0}; + AppState state = {0}; + if (!_app_init(&api, &state, PLATFORM_API)) { + return EXIT_FAILURE; + } + + // Main loop. + for (;;) { + if (!_app_reload(&api, &state, PLATFORM_API)) { + continue; + } + if (!api.step(&state, PLATFORM_API)) { + break; + } + } + + // Cleanup. + _app_destroy(&api, &state, PLATFORM_API); + return EXIT_SUCCESS; +} diff --git a/src/platform.h b/src/platform.h new file mode 100644 index 0000000..877453c --- /dev/null +++ b/src/platform.h @@ -0,0 +1,25 @@ +#ifndef MIC_PLATFORM_H +#define MIC_PLATFORM_H + +// Function pointers for the PlatformAPI. This allows the app to call platform +// specific functions that perform IO, memory allocations, etc. +typedef struct PlatformAPI { + // Reads an entire file into a null terminated buffer. It doesn't perform + // memory allocations and may crash if there is not enough memory or if it + // is uninitialized. Returns the number of bytes read. + size_t (*read_file)(const char *path, char *memory); + + // Custom memory allocation functions for the platform. + void *(*malloc)(size_t size); + void (*free)(void *ptr); + void *(*calloc)(size_t nmemb, size_t size); + void *(*realloc)(void *ptr, size_t size); + + // Sleep/wait for a given number of microseconds. + void (*sleep)(size_t microseconds); + + // Logging functions. + void (*log)(const char *format, ...); +} PlatformAPI; + +#endif // MIC_PLATFORM_H diff --git a/src/platform_posix.c b/src/platform_posix.c new file mode 100644 index 0000000..7c2fb6c --- /dev/null +++ b/src/platform_posix.c @@ -0,0 +1,171 @@ +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include + +#include "app.h" + +// +// PlatformAPI Implementation. +// + +size_t +platform_read_file(const char *path, char *memory) { + size_t file_size = 0; + FILE *fp = fopen(path, "rb"); + if (!fp) { + return 0; + } + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + rewind(fp); + fread(memory, 1, file_size, fp); + fclose(fp); + memory[file_size] = '\0'; + return file_size; +} + +void +platform_log(const char *format, ...) { + // Print date. + time_t raw_time = time(NULL); + struct tm *tm = localtime(&raw_time); + char date[64]; + strftime(date, sizeof(date), "%Y-%M-%d | %H:%M:%S", tm); + printf("%s | ", date); + + // Print message. + va_list args; + va_start(args, format); + vprintf(format, args); + + printf("\n"); + va_end(args); +} + +void +platform_sleep(size_t microseconds) { + usleep(microseconds); +} + +#if defined(LIB_NAME) && defined(LIB_DIR) + +// +// Dynamic linking with hot code reload. +// + +#include +#include +#include + +static ino_t lib_id; +static void * lib_handle; + +static const char *lib_path = LIB_DIR "/" LIB_NAME; + +static bool +load_lib(AppAPI *api) { + struct stat st; + if (stat(lib_path, &st) != 0) { + return false; + } + + if (lib_id != st.st_ino) { + void *handle = dlopen(lib_path, RTLD_NOW); + if (!handle) { + lib_handle = NULL; + lib_id = 0; + return false; + } + lib_handle = handle; + lib_id = st.st_ino; + + const AppAPI *app_api = dlsym(lib_handle, "APP_API"); + if (app_api == NULL) { + dlclose(lib_handle); + lib_handle = NULL; + lib_id = 0; + return false; + } + *api = *app_api; + } + + return true; +} + +static bool +_app_reload(AppAPI *api, AppState *state, PlatformAPI platform) { + struct stat st; + if (stat(lib_path, &st) == 0 && lib_id != st.st_ino) { + if (lib_handle) { + api->unload(state, platform); + dlclose(lib_handle); + } + + if (!load_lib(api)) { + return false; + } + api->reload(state, platform); + } + + return true; +} + +static bool +_app_init(AppAPI *api, AppState *state, PlatformAPI platform) { + if (!load_lib(api)) { + fprintf(stderr, "error: can't open app library file: %s\n", lib_path); + return false; + } + api->init(state, platform); + return true; +} + +static void +_app_destroy(AppAPI *api, AppState *state, PlatformAPI platform) { + api->destroy(state, platform); + if (lib_handle) { + dlclose(lib_handle); + lib_handle = NULL; + lib_id = 0; + } +} + +#else + +// +// Static linking of app code. +// + +extern const AppAPI APP_API; + +static bool +_app_reload() { + return true; +} + +static bool +_app_init(AppAPI *api, AppState *state, PlatformAPI platform) { + *api = APP_API; + api->init(state, platform); + return true; +} + +static void +_app_destroy(AppAPI *api, AppState *state, PlatformAPI platform) { + api->destroy(state, platform); +} + +#endif + +const PlatformAPI PLATFORM_API = { + .read_file = platform_read_file, + .malloc = malloc, + .free = free, + .calloc = calloc, + .realloc = realloc, + .log = platform_log, + .sleep = platform_sleep, +}; diff --git a/src/shorthand.h b/src/shorthand.h new file mode 100644 index 0000000..9c2e2f0 --- /dev/null +++ b/src/shorthand.h @@ -0,0 +1,35 @@ +#ifndef MIC_SHORTHAND_H +#define MIC_SHORTHAND_H + +#include +#include +#include +#include + +// +// This simple header just typedefs the basic C define types to a shorter name, +// loads the quality of life bool macro for _Bool and defines shorthand macros +// for byte sizes. We need that the targeted architecture uses the floating +// point representation as described on the IEEE-754 standard. +// + +_Static_assert(sizeof(double) == 8, "no support for IEEE-754"); +_Static_assert(sizeof(float) == 4, "no support for IEEE-754"); + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +typedef float f32; +typedef double f64; + +#define KB(N) ((u64)(N) * 1024) +#define MB(N) ((u64)KB(N) * 1024) +#define GB(N) ((u64)MB(N) * 1024) +#define TB(N) ((u64)GB(N) * 1024) + +#endif // MIC_SHORTHAND_H -- cgit v1.2.1