aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-01-25 22:01:00 +0100
committerBad Diode <bd@badd10de.dev>2021-01-25 22:01:00 +0100
commite24186fc1917c5e8b91924af0c3c7c55816ff5d6 (patch)
tree6b20f24824736e822ba614811f312a7c7c2fd43b /src
downloadmic-e24186fc1917c5e8b91924af0c3c7c55816ff5d6.tar.gz
mic-e24186fc1917c5e8b91924af0c3c7c55816ff5d6.zip
Introducing MIC
Diffstat (limited to 'src')
-rw-r--r--src/app.c44
-rw-r--r--src/app.h37
-rw-r--r--src/main.c25
-rw-r--r--src/platform.h25
-rw-r--r--src/platform_posix.c171
-rw-r--r--src/shorthand.h35
6 files changed, 337 insertions, 0 deletions
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 @@
1#include "app.h"
2#include "platform.h"
3
4static inline bool
5app_init(AppState *state, PlatformAPI platform) {
6 platform.log("INIT");
7 state->lt_memory = platform.calloc(LT_MEMORY_SIZE, sizeof(u8));
8 state->st_memory = platform.calloc(ST_MEMORY_SIZE, sizeof(u8));
9 return true;
10}
11
12static inline void
13app_destroy(AppState *state, PlatformAPI platform) {
14 (void)state; // Unused parameter.
15 platform.log("DESTROY");
16}
17
18static inline void
19app_reload(AppState *state, PlatformAPI platform) {
20 (void)state; // Unused parameter.
21 platform.log("RELOAD");
22}
23
24static inline void
25app_unload(AppState *state, PlatformAPI platform) {
26 (void)state; // Unused parameter.
27 platform.log("UNLOAD");
28}
29
30static inline bool
31app_step(AppState *state, PlatformAPI platform) {
32 (void)state; // Unused parameter.
33 platform.log("STEP");
34 platform.sleep(100000);
35 return true;
36}
37
38const AppAPI APP_API = {
39 .init = app_init,
40 .destroy = app_destroy,
41 .reload = app_reload,
42 .step = app_step,
43 .unload = app_unload,
44};
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 @@
1#ifndef MIC_APP_H
2#define MIC_APP_H
3
4#include "shorthand.h"
5#include "platform.h"
6
7#define LT_MEMORY_SIZE GB(2)
8#define ST_MEMORY_SIZE MB(100)
9
10typedef struct AppState {
11 // Long and short term memory.
12 char *lt_memory;
13 char *st_memory;
14} AppState;
15
16// Function pointers for the AppAPI.
17typedef struct AppAPI {
18 // Initialization code. Meant to be called exactly once before other API
19 // interactions. Returns the success or failure of the initialization
20 // process.
21 bool (*init)(AppState *state, PlatformAPI platform);
22
23 // Resource deallocation and cleanup. Meant to be called exactly once at the
24 // end of the app.
25 void (*destroy)(AppState *state, PlatformAPI platform);
26
27 // In case of hot code reloading, these functions handle the necessary
28 // resource setup and reloading.
29 void (*reload)(AppState *state, PlatformAPI platform);
30 void (*unload)(AppState *state, PlatformAPI platform);
31
32 // Main update/step function for the app. Returns if the app should keep
33 // running.
34 bool (*step)(AppState *state, PlatformAPI platform);
35} AppAPI;
36
37#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 @@
1#include "platform_posix.c"
2
3int
4main(void) {
5 // App initialization.
6 AppAPI api = {0};
7 AppState state = {0};
8 if (!_app_init(&api, &state, PLATFORM_API)) {
9 return EXIT_FAILURE;
10 }
11
12 // Main loop.
13 for (;;) {
14 if (!_app_reload(&api, &state, PLATFORM_API)) {
15 continue;
16 }
17 if (!api.step(&state, PLATFORM_API)) {
18 break;
19 }
20 }
21
22 // Cleanup.
23 _app_destroy(&api, &state, PLATFORM_API);
24 return EXIT_SUCCESS;
25}
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 @@
1#ifndef MIC_PLATFORM_H
2#define MIC_PLATFORM_H
3
4// Function pointers for the PlatformAPI. This allows the app to call platform
5// specific functions that perform IO, memory allocations, etc.
6typedef struct PlatformAPI {
7 // Reads an entire file into a null terminated buffer. It doesn't perform
8 // memory allocations and may crash if there is not enough memory or if it
9 // is uninitialized. Returns the number of bytes read.
10 size_t (*read_file)(const char *path, char *memory);
11
12 // Custom memory allocation functions for the platform.
13 void *(*malloc)(size_t size);
14 void (*free)(void *ptr);
15 void *(*calloc)(size_t nmemb, size_t size);
16 void *(*realloc)(void *ptr, size_t size);
17
18 // Sleep/wait for a given number of microseconds.
19 void (*sleep)(size_t microseconds);
20
21 // Logging functions.
22 void (*log)(const char *format, ...);
23} PlatformAPI;
24
25#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 @@
1#define _DEFAULT_SOURCE
2#include <stdarg.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <time.h>
6#include <unistd.h>
7
8#include "app.h"
9
10//
11// PlatformAPI Implementation.
12//
13
14size_t
15platform_read_file(const char *path, char *memory) {
16 size_t file_size = 0;
17 FILE *fp = fopen(path, "rb");
18 if (!fp) {
19 return 0;
20 }
21 fseek(fp, 0, SEEK_END);
22 file_size = ftell(fp);
23 rewind(fp);
24 fread(memory, 1, file_size, fp);
25 fclose(fp);
26 memory[file_size] = '\0';
27 return file_size;
28}
29
30void
31platform_log(const char *format, ...) {
32 // Print date.
33 time_t raw_time = time(NULL);
34 struct tm *tm = localtime(&raw_time);
35 char date[64];
36 strftime(date, sizeof(date), "%Y-%M-%d | %H:%M:%S", tm);
37 printf("%s | ", date);
38
39 // Print message.
40 va_list args;
41 va_start(args, format);
42 vprintf(format, args);
43
44 printf("\n");
45 va_end(args);
46}
47
48void
49platform_sleep(size_t microseconds) {
50 usleep(microseconds);
51}
52
53#if defined(LIB_NAME) && defined(LIB_DIR)
54
55//
56// Dynamic linking with hot code reload.
57//
58
59#include <dlfcn.h>
60#include <sys/stat.h>
61#include <sys/types.h>
62
63static ino_t lib_id;
64static void * lib_handle;
65
66static const char *lib_path = LIB_DIR "/" LIB_NAME;
67
68static bool
69load_lib(AppAPI *api) {
70 struct stat st;
71 if (stat(lib_path, &st) != 0) {
72 return false;
73 }
74
75 if (lib_id != st.st_ino) {
76 void *handle = dlopen(lib_path, RTLD_NOW);
77 if (!handle) {
78 lib_handle = NULL;
79 lib_id = 0;
80 return false;
81 }
82 lib_handle = handle;
83 lib_id = st.st_ino;
84
85 const AppAPI *app_api = dlsym(lib_handle, "APP_API");
86 if (app_api == NULL) {
87 dlclose(lib_handle);
88 lib_handle = NULL;
89 lib_id = 0;
90 return false;
91 }
92 *api = *app_api;
93 }
94
95 return true;
96}
97
98static bool
99_app_reload(AppAPI *api, AppState *state, PlatformAPI platform) {
100 struct stat st;
101 if (stat(lib_path, &st) == 0 && lib_id != st.st_ino) {
102 if (lib_handle) {
103 api->unload(state, platform);
104 dlclose(lib_handle);
105 }
106
107 if (!load_lib(api)) {
108 return false;
109 }
110 api->reload(state, platform);
111 }
112
113 return true;
114}
115
116static bool
117_app_init(AppAPI *api, AppState *state, PlatformAPI platform) {
118 if (!load_lib(api)) {
119 fprintf(stderr, "error: can't open app library file: %s\n", lib_path);
120 return false;
121 }
122 api->init(state, platform);
123 return true;
124}
125
126static void
127_app_destroy(AppAPI *api, AppState *state, PlatformAPI platform) {
128 api->destroy(state, platform);
129 if (lib_handle) {
130 dlclose(lib_handle);
131 lib_handle = NULL;
132 lib_id = 0;
133 }
134}
135
136#else
137
138//
139// Static linking of app code.
140//
141
142extern const AppAPI APP_API;
143
144static bool
145_app_reload() {
146 return true;
147}
148
149static bool
150_app_init(AppAPI *api, AppState *state, PlatformAPI platform) {
151 *api = APP_API;
152 api->init(state, platform);
153 return true;
154}
155
156static void
157_app_destroy(AppAPI *api, AppState *state, PlatformAPI platform) {
158 api->destroy(state, platform);
159}
160
161#endif
162
163const PlatformAPI PLATFORM_API = {
164 .read_file = platform_read_file,
165 .malloc = malloc,
166 .free = free,
167 .calloc = calloc,
168 .realloc = realloc,
169 .log = platform_log,
170 .sleep = platform_sleep,
171};
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 @@
1#ifndef MIC_SHORTHAND_H
2#define MIC_SHORTHAND_H
3
4#include <assert.h>
5#include <stdbool.h>
6#include <stddef.h>
7#include <stdint.h>
8
9//
10// This simple header just typedefs the basic C define types to a shorter name,
11// loads the quality of life bool macro for _Bool and defines shorthand macros
12// for byte sizes. We need that the targeted architecture uses the floating
13// point representation as described on the IEEE-754 standard.
14//
15
16_Static_assert(sizeof(double) == 8, "no support for IEEE-754");
17_Static_assert(sizeof(float) == 4, "no support for IEEE-754");
18
19typedef uint8_t u8;
20typedef uint16_t u16;
21typedef uint32_t u32;
22typedef uint64_t u64;
23typedef int8_t s8;
24typedef int16_t s16;
25typedef int32_t s32;
26typedef int64_t s64;
27typedef float f32;
28typedef double f64;
29
30#define KB(N) ((u64)(N) * 1024)
31#define MB(N) ((u64)KB(N) * 1024)
32#define GB(N) ((u64)MB(N) * 1024)
33#define TB(N) ((u64)GB(N) * 1024)
34
35#endif // MIC_SHORTHAND_H