aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--1bitjam.c50
-rw-r--r--Makefile64
-rw-r--r--asset.c10
-rw-r--r--asset.h11
-rw-r--r--config.h15
-rw-r--r--convimg.c98
-rw-r--r--error.h11
-rw-r--r--intermediate/guy.bmpbin0 -> 394 bytes
-rw-r--r--intermediate/hello.bmpbin0 -> 4234 bytes
-rw-r--r--maths.c36
-rw-r--r--maths.h22
-rw-r--r--memory.c225
-rw-r--r--memory.h56
-rw-r--r--packassets.c126
-rw-r--r--plat.c429
-rw-r--r--plat.h76
-rw-r--r--rect.c85
-rw-r--r--rect.h13
-rw-r--r--render.c97
-rw-r--r--render.h29
21 files changed, 1462 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..00f7063
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+tags
+
+*.d
+*.o
+pack.h
+/data/
+/convimg
+/packassets
+/1bitjam
diff --git a/1bitjam.c b/1bitjam.c
new file mode 100644
index 0000000..d573fe1
--- /dev/null
+++ b/1bitjam.c
@@ -0,0 +1,50 @@
+#include "asset.h"
+#include "config.h"
+#include "memory.h"
+#include "plat.h"
+#include "rect.h"
+#include "render.h"
+
+int entrypoint(int argc, const char** argv, Arena* m) {
+ Heap h;
+ App* a;
+ FPS f;
+ Renderer r;
+ Rect rect = { 0, 0, 32, 32 };
+ Rect rect2 = { 0, 0, 8, 8 };
+ int x = 0, y = 0;
+ (void)argc;
+ (void)argv;
+ init_heap(
+ &h,
+ arena_alloc(m, app_memory_size),
+ app_memory_size
+ );
+ a = new_app(&h, "1 bit jam");
+ init_fps(&f, default_mpf);
+ while (a->o) {
+ fps_begin(&f);
+ while (f.now >= f.next && a->o) {
+ app_begin(a);
+ ren_begin(&r, a->fb, viewport_w, viewport_h);
+ ren_clear(&r);
+ if (a->btn_states[btn_left] & btn_state_pressed)
+ x--;
+ if (a->btn_states[btn_right] & btn_state_pressed)
+ x++;
+ if (a->btn_states[btn_up] & btn_state_pressed)
+ y--;
+ if (a->btn_states[btn_down] & btn_state_pressed)
+ y++;
+ ren_text(&r, 30, 60, "Hello");
+ ren_map(&r, 10, 5, &rect, get_bitmap(asset_id_hello_img));
+ ren_map(&r, x, y, &rect2, get_bitmap(asset_id_guy_img));
+ ren_end(&r);
+ app_end(a);
+ fps_update(&f);
+ }
+ fps_end(&f);
+ }
+ deinit_app(a);
+ return error_none;
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bf15054
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,64 @@
+compiler = gcc
+tool_compiler = gcc
+linker = gcc
+cflags = -I./ -g -DDEBUG -Dplat_x11 -Dplat_x86 \
+-Dplat_posix -Dallocation_default_alignment=8 \
+-Wall -Wextra -pedantic -std=c90
+lflags = -lX11 -lXi
+target = 1bitjam
+int_dir = intermediate
+data_dir = data
+convimg = convimg
+packassets = packassets
+pack = pack.h
+
+sources = \
+ 1bitjam.c \
+ asset.c \
+ maths.c \
+ memory.c \
+ plat.c \
+ rect.c \
+ render.c \
+
+image_sources = \
+ $(int_dir)/guy.bmp \
+ $(int_dir)/hello.bmp
+
+objects = $(sources:%.c=%.o)
+images = $(image_sources:$(int_dir)/%.bmp=$(data_dir)/%.img)
+
+all: $(target) $(pack)
+
+$(objects): %.o : %.c | $(pack)
+ $(compiler) -MMD -MF $(basename $@).d $(cflags) -o $@ -c $<
+
+$(images): $(data_dir)/%.img : $(int_dir)/%.bmp | $(convimg) $(data_dir)
+ ./$(convimg) $< $@
+
+$(pack): $(packassets) $(images)
+ ./$(packassets) \
+ $(pack) \
+ $(data_dir) \
+ $(notdir $(images))
+
+$(convimg): convimg.c
+ $(tool_compiler) convimg.c $(cflags) -o $@
+
+$(packassets): packassets.c
+ $(tool_compiler) packassets.c $(cflags) -o $@
+
+$(target): $(objects)
+ $(linker) $(objects) -o $@ $(lflags)
+
+$(data_dir):
+ mkdir -p $(data_dir)
+
+clean:
+ rm *.d
+ rm *.o
+ rm $(pack)
+ rm -r $(data_dir)
+ rm $(target)
+
+-include $(sources:%.c=%.d)
diff --git a/asset.c b/asset.c
new file mode 100644
index 0000000..78085ec
--- /dev/null
+++ b/asset.c
@@ -0,0 +1,10 @@
+#include "pack.h"
+#include "asset.h"
+
+const unsigned char* get_asset(Asset_ID id) {
+ return &asset_data[asset_offset_table[id]];
+}
+
+const struct Bitmap* get_bitmap(Asset_ID id) {
+ return (const struct Bitmap*)get_asset(id);
+}
diff --git a/asset.h b/asset.h
new file mode 100644
index 0000000..74cf66b
--- /dev/null
+++ b/asset.h
@@ -0,0 +1,11 @@
+#ifndef asset_h
+#define asset_h
+
+#define enum_only
+#include "pack.h"
+#undef enum_only
+
+const unsigned char* get_asset(Asset_ID id);
+const struct Bitmap* get_bitmap(Asset_ID id);
+
+#endif
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..ed03b67
--- /dev/null
+++ b/config.h
@@ -0,0 +1,15 @@
+#ifndef config_h
+#define config_h
+
+#define memory_size (1024 * 8)
+#define app_memory_size (1024 * 4)
+
+#define max_pc_window_w 3000
+#define max_pc_window_h 3000
+
+#define viewport_w 128
+#define viewport_h 128
+#define default_scale 3
+#define default_mpf 50
+
+#endif
diff --git a/convimg.c b/convimg.c
new file mode 100644
index 0000000..631a8b1
--- /dev/null
+++ b/convimg.c
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+ unsigned char r, g, b, a;
+} Colour;
+
+int main(int argc, char** argv) {
+ FILE* outfile, * infile;
+ char bmp_magic[2];
+ int ret = 0;
+ unsigned bmp_offset;
+ int bmp_w, bmp_h, x, y, s, bs;
+ unsigned short bmp_bits;
+ unsigned* bitmap;
+ Colour pixel, * buffer;
+ unsigned char t;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s infile outfile.\n", argv[0]);
+ return 1;
+ }
+
+ infile = fopen(argv[1], "rb");
+ if (!infile) {
+ fprintf(stderr, "Failed to open %s.\n", argv[1]);
+ return 2;
+ }
+
+ fread(bmp_magic, 2, 1, infile);
+ if (bmp_magic[0] != 'B' || bmp_magic[1] != 'M') {
+ ret = 3;
+ fprintf(stderr, "Not a valid bitmap file.\n");
+ goto end;
+ }
+
+ fseek(infile, 10, SEEK_SET);
+ fread(&bmp_offset, 4, 1, infile);
+
+ fseek(infile, 18, SEEK_SET);
+ fread(&bmp_w, 4, 1, infile);
+ fread(&bmp_h, 4, 1, infile);
+ fseek(infile, 28, SEEK_SET);
+ fread(&bmp_bits, 2, 1, infile);
+
+ if (bmp_bits != 32) {
+ ret = 4;
+ fprintf(stderr, "Bitmap must have 32 bit pixels. %d\n", bmp_bits);
+ goto end;
+ }
+
+ fseek(infile, bmp_offset, SEEK_SET);
+
+ s = bmp_w * bmp_h;
+ buffer = malloc(s * 4);
+ /* Read & flip */
+ for (y = 0; y < bmp_h; y++) {
+ for (x = 0; x < bmp_w; x++) {
+ fread(&pixel, 1, 4, infile);
+ t = pixel.r;
+ pixel.r = pixel.b;
+ pixel.b = t;
+ if (pixel.a == 0) {
+ pixel.r = 0;
+ pixel.g = 0;
+ pixel.b = 0;
+ }
+ buffer[x + (bmp_h - y - 1) * bmp_w] = pixel;
+ }
+ }
+
+ outfile = fopen(argv[2], "wb");
+ if (!outfile) {
+ fprintf(stderr, "Failed to open %s.\n", argv[2]);
+ fclose(infile);
+ return 2;
+ }
+ bs = (bmp_w * bmp_h + 32) / 32 * 4;
+ bitmap = calloc(bs, 1);
+ for (y = 0; y < bmp_h; y++) {
+ for (x = 0; x < bmp_w; x++) {
+ int idx = x + y * bmp_w;
+ if (buffer[idx].r) {
+ bitmap[idx / 32] |= 1 << (idx % 32);
+ }
+ }
+ }
+ fwrite(&bmp_w, 1, 2, outfile);
+ fwrite(&bmp_h, 1, 2, outfile);
+ fwrite(bitmap, 1, bs, outfile);
+ free(buffer);
+
+end:
+ fclose(infile);
+ fclose(outfile);
+ return ret;
+}
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..8ef761b
--- /dev/null
+++ b/error.h
@@ -0,0 +1,11 @@
+#ifndef error_h
+#define error_h
+
+typedef enum {
+ error_none,
+ error_assertion_failed,
+ error_platform_error,
+ error_out_of_memory
+} Error;
+
+#endif
diff --git a/intermediate/guy.bmp b/intermediate/guy.bmp
new file mode 100644
index 0000000..9a881e0
--- /dev/null
+++ b/intermediate/guy.bmp
Binary files differ
diff --git a/intermediate/hello.bmp b/intermediate/hello.bmp
new file mode 100644
index 0000000..433cd18
--- /dev/null
+++ b/intermediate/hello.bmp
Binary files differ
diff --git a/maths.c b/maths.c
new file mode 100644
index 0000000..3bed3e1
--- /dev/null
+++ b/maths.c
@@ -0,0 +1,36 @@
+#include "maths.h"
+
+int sin_table[sin_table_count] = {
+ 0x000, 0x00c, 0x019, 0x025, 0x032, 0x03e, 0x04b, 0x057,
+ 0x063, 0x070, 0x07c, 0x088, 0x094, 0x0a0, 0x0ac, 0x0b8,
+ 0x0c3, 0x0cf, 0x0da, 0x0e6, 0x0f1, 0x0fc, 0x107, 0x111,
+ 0x11c, 0x126, 0x130, 0x13a, 0x144, 0x14e, 0x157, 0x161,
+ 0x16a, 0x172, 0x17b, 0x183, 0x18b, 0x193, 0x19b, 0x1a2,
+ 0x1a9, 0x1b0, 0x1b7, 0x1bd, 0x1c3, 0x1c9, 0x1ce, 0x1d4,
+ 0x1d9, 0x1dd, 0x1e2, 0x1e6, 0x1e9, 0x1ed, 0x1f0, 0x1f3,
+ 0x1f6, 0x1f8, 0x1fa, 0x1fc, 0x1fd, 0x1fe, 0x1ff, 0x1ff,
+ 0x200, 0x1ff, 0x1ff, 0x1fe, 0x1fd, 0x1fc, 0x1fa, 0x1f8,
+ 0x1f6, 0x1f3, 0x1f0, 0x1ed, 0x1e9, 0x1e6, 0x1e2, 0x1dd,
+ 0x1d9, 0x1d4, 0x1ce, 0x1c9, 0x1c3, 0x1bd, 0x1b7, 0x1b0,
+ 0x1a9, 0x1a2, 0x19b, 0x193, 0x18b, 0x183, 0x17b, 0x172,
+ 0x16a, 0x161, 0x157, 0x14e, 0x144, 0x13a, 0x130, 0x126,
+ 0x11c, 0x111, 0x107, 0x0fc, 0x0f1, 0x0e6, 0x0da, 0x0cf,
+ 0x0c3, 0x0b8, 0x0ac, 0x0a0, 0x094, 0x088, 0x07c, 0x070,
+ 0x063, 0x057, 0x04b, 0x03e, 0x032, 0x025, 0x019, 0x00c,
+};
+int cos_table[sin_table_count];
+
+void init_maths(void) {
+ int i, hs = sin_table_count >> 1;
+ for (
+ i = hs;
+ i < sin_table_count;
+ i++
+ ) {
+ sin_table[i] = -sin_table[i - hs];
+ }
+ hs >>= 1;
+ for (i = 0; i < sin_table_count; i++) {
+ cos_table[i] = sin_table[(i + hs) & sin_table_mask];
+ }
+}
diff --git a/maths.h b/maths.h
new file mode 100644
index 0000000..bd87839
--- /dev/null
+++ b/maths.h
@@ -0,0 +1,22 @@
+#ifndef maths_h
+#define maths_h
+
+#define mini(a_, b_) ((a_) < (b_) ? (a_) : (b_))
+#define maxi(a_, b_) ((a_) > (b_) ? (a_) : (b_))
+#define clamp(v_, min_, max_) (maxi(min_, mini(max_, v_)))
+#define absolute(v_) ((v_) < 0? -(v_): (v_))
+
+#define fbits 0x9
+#define f1 0x200
+
+#define sin_table_count 0x100
+#define sin_table_mask 0x0ff
+#define sqrt_table_count 0x800
+#define sqrt_table_mask 0x07f
+
+extern int sin_table[sin_table_count];
+extern int cos_table[sin_table_count];
+
+void init_maths(void);
+
+#endif
diff --git a/memory.c b/memory.c
new file mode 100644
index 0000000..b5c6a70
--- /dev/null
+++ b/memory.c
@@ -0,0 +1,225 @@
+#include "memory.h"
+#include "plat.h"
+#include <stdint.h>
+#include <stddef.h>
+
+int aligned(void* p, int a) {
+ return (uintptr_t)p % a == 0;
+}
+
+int align_size(int s, int a) {
+ return (s + (a - 1)) & -a;
+}
+
+static uintptr_t align_address(
+ uintptr_t ad,
+ size_t al
+) {
+ size_t m;
+ m = al - 1;
+ return (ad + m) & ~m;
+}
+
+void init_arena(
+ Arena* a,
+ void* mem,
+ int size
+) {
+ a->buf = mem;
+ a->size = size;
+ a->ptr = 0;
+}
+
+void clear_arena(Arena* a) {
+ a->ptr = 0;
+}
+
+void* imp_arena_alloc(
+ Arena* a,
+ int size
+) {
+ char* r;
+ assert(a->ptr + size < a->size);
+ r = &a->buf[a->ptr];
+ a->ptr += size;
+ return r;
+}
+
+void* arena_alloc(
+ Arena* a,
+ int size
+) {
+ return arena_alloc_aligned(
+ a,
+ size,
+ allocation_default_alignment
+ );
+}
+
+void* arena_alloc_aligned(
+ Arena* a,
+ int size,
+ int align
+) {
+ void* p;
+ p = imp_arena_alloc(
+ a,
+ size + align + 1
+ );
+ return (void*)align_address((uintptr_t)p, align);
+}
+
+void init_heap(
+ Heap* h,
+ void* mem,
+ int size
+) {
+ int* fb;
+ assert(aligned(mem, 4));
+ assert(size > 8);
+ h->buf = mem;
+ h->size = size;
+ h->blocks = 1;
+ fb = (int*)h->buf;
+ fb[0] = size;
+}
+
+void* imp2_heap_alloc(
+ Heap* h,
+ int size
+) {
+ int o, i;
+ int hs = sizeof(int);
+ int as = align_size(size + hs, hs);
+ int f = ~((unsigned)-1 >> 1);
+ for (i = o = 0; i < h->blocks; i++) {
+ int* phdr = (int*)&h->buf[o];
+ int hdr = *phdr, bs;
+ assert(aligned(phdr, sizeof hdr));
+ bs = hdr & ~f;
+ if (~hdr & f) {
+ if (as == bs) {
+ phdr[0] |= 1;
+ return phdr + 1;
+ } else {
+ int ns = bs - as;
+ if (ns > hs) {
+ int* nhdr = &phdr[ns / 4];
+ phdr[0] = ns;
+ nhdr[0] = as | f;
+ h->blocks++;
+ return &nhdr[1];
+ }
+ }
+ } else
+ o += bs;
+ }
+ return 0;
+}
+
+void* imp_heap_alloc(Heap* h, int s) {
+ void* p = imp2_heap_alloc(h, s);
+ if (!p) {
+ heap_defrag(h);
+ p = imp2_heap_alloc(h, s);
+ }
+ return p;
+}
+
+void imp_heap_free(Heap* h, void* p) {
+ assert((char*)p > h->buf);
+ assert((char*)p < h->buf + h->size);
+ (void)h;
+ ((int*)p)[-1] &= (unsigned)-1 >> 1;
+}
+
+void* heap_alloc_aligned(
+ Heap* h,
+ int size,
+ int align
+) {
+ unsigned char* p, * a;
+ ptrdiff_t shift;
+ size += (int)align;
+ p = imp_heap_alloc(h, size);
+ if (!p) { return 0; }
+ a = (unsigned char*)align_address((uintptr_t)p, align);
+ a += align * (unsigned)(p == a);
+ shift = a - p;
+ a[-1] = shift & 0xff;
+ return a;
+}
+
+void heap_free_aligned(Heap* h, void* p) {
+ unsigned char* a;
+ ptrdiff_t shift;
+ a = p;
+ shift = a[-1];
+ shift += 256 * shift == 0;
+ a -= shift;
+ imp_heap_free(h, a);
+}
+
+void heap_defrag(Heap* h) {
+ int i, o, mtc;
+ int f = ~((unsigned)-1 >> 1);
+ for (i = o = mtc = 0; i < h->blocks; i++) {
+ int* phdr = (int*)&h->buf[o];
+ int hdr = *phdr, bs, m, mc;
+ assert(aligned(phdr, sizeof hdr));
+ bs = hdr & ~f;
+ if (~hdr & f) {
+ for (
+ m = bs, mc = 0, i++;
+ i < h->blocks;
+ i++, mc++
+ ) {
+ int mhdr = *(int*)&h->buf[o + m];
+ if (~mhdr & f)
+ m += mhdr & ~f;
+ else
+ break;
+ }
+ i--;
+ bs = m;
+ phdr[0] = bs;
+ mtc += mc;
+ }
+ o += bs;
+ }
+ h->blocks -= mtc;
+}
+
+void* heap_alloc(
+ Heap* h,
+ int size
+) {
+ return heap_alloc_aligned(
+ h,
+ size,
+ allocation_default_alignment
+ );
+}
+
+void heap_free(
+ Heap* h,
+ void* p
+) {
+ heap_free_aligned(h, p);
+}
+
+/*
+void print_blocks(Heap* h) {
+ int i, o;
+ int fb = ~((unsigned)-1 >> 1);
+ for (i = o = 0; i < h->blocks; i++) {
+ int b = *(int*)&h->buf[o];
+ int bs = b & ~fb;
+ int f = ~b & fb;
+ printf("%s %d\n", f? "free": " ", bs);
+ o += bs;
+ }
+ assert(o == h->size);
+}
+*/
+
diff --git a/memory.h b/memory.h
new file mode 100644
index 0000000..38e632a
--- /dev/null
+++ b/memory.h
@@ -0,0 +1,56 @@
+#ifndef memory_h
+#define memory_h
+
+int aligned(void* p, int a);
+int align_size(int s, int a);
+
+typedef struct Arena {
+ char* buf;
+ int size, ptr;
+} Arena;
+
+void init_arena(
+ Arena* a,
+ void* mem,
+ int size
+);
+void clear_arena(Arena* a);
+void* arena_alloc(
+ Arena* a,
+ int size
+);
+void* arena_alloc_aligned(
+ Arena* a,
+ int size,
+ int align
+);
+
+typedef struct Heap {
+ char* buf;
+ int size, blocks;
+} Heap;
+
+void init_heap(
+ Heap* h,
+ void* mem,
+ int size
+);
+void* heap_alloc(
+ Heap* h,
+ int size
+);
+void* heap_alloc_aligned(
+ Heap* h,
+ int size,
+ int align
+);
+void heap_free_aligned(
+ Heap* h,
+ void* p
+);
+void heap_free(
+ Heap* h,
+ void* p
+);
+void heap_defrag(Heap* h);
+#endif
diff --git a/packassets.c b/packassets.c
new file mode 100644
index 0000000..85e0ddb
--- /dev/null
+++ b/packassets.c
@@ -0,0 +1,126 @@
+#include "error.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define buffer_size 8
+
+const char* enum_header =
+"#ifndef pack_h\n"
+"#define pack_h\n"
+"typedef enum {\n";
+
+const char* enum_footer =
+"\tasset_id_count\n"
+"} Asset_ID;\n";
+
+const char* footer =
+"#endif\n";
+
+typedef struct {
+ int size;
+ int offset;
+ char name[56];
+} Entry;
+
+int main(int argc, char** argv) {
+ int i, j, file_count, read;
+ char buffer[buffer_size];
+ char name[56];
+ const char* workingdir, * dsep = "";
+ Entry entry;
+ FILE* outfile, * infile;
+ const char* filename = argv[1];
+
+ outfile = fopen(filename, "wb");
+ if (!outfile) {
+ fprintf(
+ stderr,
+ "Failed to open '%s' for writing.\n",
+ filename
+ );
+ return 1;
+ }
+
+ file_count = argc - 3;
+ workingdir = argv[2];
+ if (workingdir[strlen(workingdir) - 1] != '/')
+ dsep = "/";
+
+ fprintf(outfile, "%s", enum_header);
+ for (i = 0; i < file_count; i++) {
+ char* c;
+ strcpy(name, "asset_id_");
+ strcat(name, argv[i + 3]);
+ for (c = name; *c; c++) {
+ if (c[0] == '.') c[0] = '_';
+ }
+ fprintf(outfile, "\t%s,\n", name);
+ }
+ fprintf(outfile, "%s", enum_footer);
+ fprintf(outfile, "#ifndef enum_only\n");
+ fprintf(outfile, "const int asset_offset_table[] = {\n");
+ entry.offset = 0;
+ for (i = 0; i < file_count; i++) {
+ strcpy(entry.name, argv[i + 3]);
+ strcpy(name, workingdir);
+ strcat(name, dsep);
+ strcat(name, entry.name);
+
+ infile = fopen(name, "rb");
+ if (!infile) {
+ fprintf(
+ stderr,
+ "Failed to open '%s' for reading.\n",
+ entry.name
+ );
+ return 1;
+ }
+
+ fseek(infile, 0, SEEK_END);
+ entry.size = ftell(infile);
+ fclose(infile);
+ fprintf(outfile, "\t0x%08x,\n", entry.offset);
+ entry.offset += entry.size;
+ }
+ fprintf(outfile, "};\nconst unsigned char asset_data[] = {\n");
+ for (i = 0; i < file_count; i++) {
+ strcpy(entry.name, argv[i + 3]);
+ strcpy(name, workingdir);
+ strcat(name, dsep);
+ strcat(name, entry.name);
+
+ infile = fopen(name, "rb");
+ if (!infile) {
+ fprintf(
+ stderr,
+ "Failed to open '%s' for reading.\n",
+ entry.name
+ );
+ return 1;
+ }
+
+ fseek(infile, 0, SEEK_END);
+ entry.size = ftell(infile);
+ rewind(infile);
+
+ for (j = 0; j < entry.size; j += buffer_size) {
+ int k;
+ read = fread(buffer, 1, buffer_size, infile);
+ fprintf(outfile, "\t");
+ for (k = 0; k < read; k++) {
+ fprintf(outfile, "0x%02x, ", (unsigned char)buffer[k]);
+ }
+ fseek(outfile, ftell(outfile) - 1, SEEK_SET);
+ fprintf(outfile, "\n");
+ }
+
+ fclose(infile);
+ }
+
+ fprintf(outfile, "};\n#endif\n#endif\n");
+
+ fclose(outfile);
+
+ return 0;
+}
diff --git a/plat.c b/plat.c
new file mode 100644
index 0000000..2101910
--- /dev/null
+++ b/plat.c
@@ -0,0 +1,429 @@
+#include "config.h"
+#include "maths.h"
+#include "memory.h"
+#include "plat.h"
+
+#ifdef plat_posix
+#define _POSIX_SOURCE
+#define _GNU_SOURCE
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+extern int isatty(int);
+extern int fileno(FILE*);
+
+int imp_assert(
+ int val,
+ const char* expr,
+ const char* file,
+ int line
+) {
+ if (!val) {
+ print_err(
+ "%d:%s: Assertion failed: %s.\n",
+ line,
+ file,
+ expr
+ );
+ pbreak(error_assertion_failed);
+ return 0;
+ }
+ return 1;
+}
+
+void print(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+}
+
+void print_err(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\033[31;31m");
+ }
+
+ vfprintf(stderr, fmt, args);
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\033[0m");
+ }
+
+ va_end(args);
+}
+
+void print_war(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\033[31;35m");
+ }
+
+ vfprintf(stderr, fmt, args);
+
+ if (isatty(fileno(stderr))) {
+ fprintf(stderr, "\033[0m");
+ }
+
+ va_end(args);
+}
+
+void pbreak(Error code) {
+#if defined(DEBUG) && defined(plat_x86)
+ __asm__("int3;");
+ (void)code;
+#else
+ exit(code);
+#endif
+}
+
+static clockid_t global_clock;
+static unsigned long global_freq;
+
+void init_timer(void) {
+ struct timespec ts;
+ global_clock = CLOCK_REALTIME;
+ global_freq = 1000000000;
+#if defined(_POSIX_MONOTONIC_CLOCK)
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ global_clock = CLOCK_MONOTONIC;
+ }
+#else
+ (void)ts;
+#endif
+}
+
+unsigned long get_timer(void) {
+ struct timespec ts;
+ clock_gettime(global_clock, &ts);
+ return
+ (unsigned long)ts.tv_sec * global_freq +
+ (unsigned long)ts.tv_nsec;
+}
+
+void sleep_ns(unsigned long ns) {
+ struct timespec t = { 0 };
+ t.tv_nsec = ns;
+ nanosleep(&t, &t);
+}
+
+void init_fps(FPS* f, int mpf) {
+ f->now = get_timer();
+ f->next = f->now;
+ f->mpf = mpf;
+ f->pt = 0;
+ f->ct = -1;
+}
+
+void fps_begin(FPS* f) {
+ f->now = get_timer();
+}
+
+void fps_end(FPS* f) {
+ unsigned long ts = f->next - f->now;
+ if (ts > 0) {
+ sleep_ns(ts);
+ }
+}
+
+void fps_update(FPS* f) {
+ f->next += f->mpf * 1000000;
+ f->pt = f->ct;
+ f->ct = get_timer();
+ f->fps = 1000000000 / (f->ct - f->pt);
+}
+
+extern int entrypoint(int, const char**, Arena*);
+
+int main(int argc, const char** argv) {
+ Arena a;
+ void* mem = malloc(memory_size);
+ if (!mem) {
+ print_err("Out of memory.\n");
+ return error_out_of_memory;
+ }
+ init_timer();
+ init_arena(
+ &a,
+ mem,
+ memory_size
+ );
+ return entrypoint(argc, argv, &a);
+}
+
+#endif
+
+
+#ifdef plat_x11
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef struct {
+ unsigned char r, g, b, a;
+} Colour;
+
+typedef struct {
+ Display* d;
+ Window wi;
+ GC gc;
+ int w, h;
+ int rmx, rmy;
+ int omx, omy;
+ int ms;
+ unsigned long begin, end;
+ XImage* bb;
+ Colour* bbp;
+ Atom wm_delete;
+} App_Internal;
+
+void init_rendering(
+ App* a,
+ App_Internal* i,
+ XWindowAttributes* wa
+) {
+ Colour* p;
+ int w = max_pc_window_w;
+ int h = max_pc_window_h;
+ a->fb = heap_alloc(
+ a->heap,
+ (viewport_w * viewport_h + 32) / 32 * sizeof *a->fb
+ );
+ p = malloc(sizeof *p * w * h);
+ if (!p || !a->fb) {
+ print_err("Out of memory.\n");
+ pbreak(error_out_of_memory);
+ }
+ i->bb = XCreateImage(
+ i->d,
+ wa->visual,
+ wa->depth,
+ ZPixmap,
+ 0,
+ (char*)p,
+ w,
+ h,
+ 32,
+ w * sizeof *p
+ );
+ if (!i->bb) {
+ print_err("Failed to create X11 backbuffer.\n");
+ pbreak(error_platform_error);
+ }
+ i->bbp = p;
+}
+
+void init_window(App* a, App_Internal* i, const char* n) {
+ Window root, w;
+ Display* d;
+ XWindowAttributes wa;
+ unsigned rm, bm;
+ i->bb = 0;
+ i->bbp = 0;
+ d = i->d;
+ root = DefaultRootWindow(d);
+ w = XCreateSimpleWindow(
+ d,
+ root,
+ 0,
+ 0,
+ viewport_w * default_scale,
+ viewport_h * default_scale,
+ 0,
+ WhitePixel(d, 0),
+ BlackPixel(d, 0)
+ );
+ i->wi = w;
+ i->wm_delete = XInternAtom(
+ d,
+ "WM_DELETE_WINDOW",
+ 0
+ );
+ XSetWMProtocols(d, w, &i->wm_delete, 1);
+ XStoreName(d, w, n);
+ XSelectInput(
+ d,
+ w,
+ ExposureMask |
+ KeyPressMask |
+ KeyReleaseMask |
+ PointerMotionMask |
+ ButtonPressMask |
+ ButtonReleaseMask
+ );
+ XMapRaised(d, w);
+ i->gc = XCreateGC(d, w, 0, 0);
+ if (!i->gc) {
+ print_err("Failed to create X graphics context.\n");
+ pbreak(error_platform_error);
+ }
+ XGetWindowAttributes(d, w, &wa);
+ if (wa.depth != 24 && wa.depth != 32) {
+ print_err("Only true colour displays are supported.\n");
+ pbreak(error_platform_error);
+ }
+ rm = wa.visual->red_mask & 0x1000000;
+ bm = wa.visual->blue_mask & 0xffffff;
+ if ((rm == 0xff && bm == 0xff0000)) {
+ print_war("Detected BGR. Colours will look fucked.\n");
+ }
+ init_rendering(a, i, &wa);
+ i->w = wa.width;
+ i->h = wa.height;
+}
+
+Btn btn_from_xkey(unsigned key) {
+ switch (key) {
+ case XK_Up: return btn_up;
+ case XK_Down: return btn_down;
+ case XK_Left: return btn_left;
+ case XK_Right: return btn_right;
+ case 0x78: return btn_shoot;
+ case 0x7a: return btn_jump;
+ case XK_Return: return btn_start;
+ default: return btn_unknown;
+ }
+}
+
+App* new_app(Heap* mem, const char* n) {
+ App* a;
+ App_Internal* i;
+ a = heap_alloc(mem, sizeof *a + sizeof *i);
+ i = (App_Internal*)(&a[1]);
+ a->heap = mem;
+ a->s = 2;
+ a->o = 0;
+ a->err = error_none;
+ i->d = XOpenDisplay(0);
+ i->omx = i->omy = 0;
+ i->ms = 1;
+ if (!i->d) {
+ print_err("Failed to open X11 display.\n");
+ pbreak(error_platform_error);
+ }
+ init_window(a, i, n);
+ a->o = 1;
+ return a;
+}
+
+void deinit_app(App* a) {
+ App_Internal* i = (App_Internal*)(&a[1]);
+ XDestroyImage(i->bb);
+ XFreeGC(i->d, i->gc);
+ XDestroyWindow(i->d, i->wi);
+ XCloseDisplay(i->d);
+ heap_free(a->heap, a);
+}
+
+void app_begin(App* a) {
+ App_Internal* i = (App_Internal*)(&a[1]);
+ Display* d = i->d;
+ Window w = i->wi;
+ int j;
+ i->begin = get_timer();
+ for (j = 0; j < btn_count; j++)
+ a->btn_states[j] &= ~(
+ btn_state_just_pressed | btn_state_just_released
+ );
+ while (XPending(d)) {
+ XEvent e;
+ XWindowAttributes wa;
+ KeySym sym;
+ Btn btn;
+ XNextEvent(d, &e);
+ switch (e.type) {
+ case ClientMessage:
+ if (
+ (Atom)e.xclient.data.l[0] ==
+ i->wm_delete
+ ) {
+ a->o = 0;
+ }
+ break;
+ case Expose:
+ XGetWindowAttributes(d, w, &wa);
+ i->w = mini(wa.width, max_pc_window_w);
+ i->h = mini(wa.height, max_pc_window_h);
+ break;
+ case KeyPress:
+ sym = XLookupKeysym(&e.xkey, 0);
+ btn = btn_from_xkey(sym);
+ a->btn_states[btn] |=
+ btn_state_pressed |
+ btn_state_just_pressed;
+ break;
+ case KeyRelease:
+ sym = XLookupKeysym(&e.xkey, 0);
+ btn = btn_from_xkey(sym);
+ a->btn_states[btn] &= ~btn_state_pressed;
+ a->btn_states[btn] |= btn_state_just_released;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void app_rencpy(
+ App* a,
+ App_Internal* i,
+ int sx,
+ int sy,
+ int s
+) {
+ const Colour white = { 0xff, 0xff, 0xff, 0xff };
+ const Colour black = { 0x00, 0x00, 0x00, 0xff };
+ int x, y, sj = 0;
+ int w = viewport_w * s;
+ int h = viewport_h * s;
+ int ex = sx + w;
+ int ey = sy + h;
+ for (y = sy; y < ey; y++, sj++) {
+ int sy = sj / s;
+ int si = 0;
+ for (x = sx; x < ex; x++, si++) {
+ int sx = si / s;
+ int idx = sx + sy * viewport_w;
+ int bit = 1 << (idx & 0x1f);
+ bit &= a->fb[idx >> 5];
+ i->bbp[x + y * max_pc_window_w] = bit? white: black;
+ }
+ }
+ XPutImage(
+ i->d,
+ i->wi,
+ i->gc,
+ i->bb,
+ 0, 0,
+ sx, sy,
+ w, h
+ );
+}
+
+void app_end(App* a) {
+ int s = 1;
+ int m1, m2;
+ App_Internal* i = (App_Internal*)(&a[1]);
+ if (i->w < i->h) {
+ m1 = i->w;
+ m2 = viewport_w;
+ } else {
+ m1 = i->h;
+ m2 = viewport_h;
+ }
+ while (m2 * s < m1) s++; /* lol */
+ app_rencpy(a, i, 0, 0, s);
+ i->end = get_timer();
+ a->fps = 1000000000 / (i->end - i->begin);
+}
+
+#endif
diff --git a/plat.h b/plat.h
new file mode 100644
index 0000000..27d8d0a
--- /dev/null
+++ b/plat.h
@@ -0,0 +1,76 @@
+#ifndef plat_h
+#define plat_h
+
+#include "error.h"
+
+struct Heap;
+
+#ifdef assert
+#undef assert
+#endif
+
+#define assert(expr) \
+ imp_assert( \
+ expr, \
+ #expr, \
+ __FILE__, \
+ __LINE__ \
+ )
+
+int imp_assert(
+ int val,
+ const char* expr,
+ const char* file,
+ int line
+);
+
+void print(const char* fmt, ...);
+void print_err(const char* fmt, ...);
+void print_war(const char* fmt, ...);
+void pbreak(Error code);
+
+typedef struct {
+ int mpf;
+ int fps;
+ unsigned long pt, ct;
+ unsigned long now, next;
+} FPS;
+
+void init_fps(FPS* f, int mpf);
+void fps_begin(FPS* f);
+void fps_end(FPS* f);
+void fps_update(FPS* f);
+
+typedef enum {
+ btn_unknown,
+ btn_left,
+ btn_right,
+ btn_up,
+ btn_down,
+ btn_shoot,
+ btn_jump,
+ btn_start,
+ btn_count
+} Btn;
+
+typedef enum {
+ btn_state_pressed = 1 << 0,
+ btn_state_just_pressed = 1 << 1,
+ btn_state_just_released = 1 << 2
+} Key_State;
+
+typedef struct App {
+ int o, s;
+ int fps;
+ Error err;
+ unsigned* fb;
+ struct Heap* heap;
+ unsigned char btn_states[btn_count];
+} App;
+
+App* new_app(struct Heap* mem, const char* n);
+void deinit_app(App* a);
+void app_begin(App* a);
+void app_end(App* a);
+
+#endif
diff --git a/rect.c b/rect.c
new file mode 100644
index 0000000..69758c1
--- /dev/null
+++ b/rect.c
@@ -0,0 +1,85 @@
+#include "rect.h"
+
+Rect* rect_clip(Rect* r, const Rect* c) {
+ int n;
+ if ((n = c->x - r->x) > 0) {
+ r->w -= n;
+ r->x += n;
+ }
+ if ((n = c->y - r->y) > 0) {
+ r->h -= n;
+ r->y += n;
+ }
+ if ((n = r->x + r->w - (c->x + c->w)) > 0)
+ r->w -= n;
+ if ((n = r->y + r->h - (c->y + c->h)) > 0)
+ r->h -= n;
+ return r;
+}
+
+Rect* rect_clipr(Rect* r, const int* c) {
+ int n;
+ if ((n = c[0] - r->x) > 0) {
+ r->w -= n;
+ r->x += n;
+ }
+ if ((n = c[1] - r->y) > 0) {
+ r->h -= n;
+ r->y += n;
+ }
+ if ((n = r->x + r->w - c[2]) > 0)
+ r->w -= n;
+ if ((n = r->y + r->h - c[3]) > 0)
+ r->h -= n;
+ return r;
+}
+
+Rect* rect_clips(Rect* r, Rect* s, const Rect* c) {
+ int n;
+ if ((n = c->x - r->x) > 0) {
+ r->w -= n;
+ r->x += n;
+ s->w -= n;
+ s->x += n;
+ }
+ if ((n = c->y - r->y) > 0) {
+ r->h -= n;
+ r->y += n;
+ s->h -= n;
+ s->y += n;
+ }
+ if ((n = r->x + r->w - (c->x + c->w)) > 0) {
+ r->w -= n;
+ s->w -= n;
+ }
+ if ((n = r->y + r->h - (c->y + c->h)) > 0) {
+ r->h -= n;
+ s->h -= n;
+ }
+ return r;
+}
+
+Rect* rect_clipsr(Rect* r, Rect* s, const int* c) {
+ int n;
+ if ((n = c[0] - r->x) > 0) {
+ r->w -= n;
+ r->x += n;
+ s->w -= n;
+ s->x += n;
+ }
+ if ((n = c[1] - r->y) > 0) {
+ r->h -= n;
+ r->y += n;
+ s->h -= n;
+ s->y += n;
+ }
+ if ((n = r->x + r->w - c[2]) > 0) {
+ r->w -= n;
+ s->w -= n;
+ }
+ if ((n = r->y + r->h - c[3]) > 0) {
+ r->h -= n;
+ s->h -= n;
+ }
+ return r;
+}
diff --git a/rect.h b/rect.h
new file mode 100644
index 0000000..5c89aec
--- /dev/null
+++ b/rect.h
@@ -0,0 +1,13 @@
+#ifndef rect_h
+#define rect_h
+
+typedef struct Rect {
+ int x, y, w, h;
+} Rect;
+
+Rect* rect_clip(Rect* r, const Rect* c);
+Rect* rect_clipr(Rect* r, const int* c);
+Rect* rect_clips(Rect* r, Rect* s, const Rect* c);
+Rect* rect_clipsr(Rect* r, Rect* s, const int* c);
+
+#endif
diff --git a/render.c b/render.c
new file mode 100644
index 0000000..34416c0
--- /dev/null
+++ b/render.c
@@ -0,0 +1,97 @@
+#include "rect.h"
+#include "render.h"
+
+static int smol_font_w = 384;
+static unsigned smol_font_data[] = {
+ 0x2296a520, 0x40000524, 0x77757767, 0x60002077,
+ 0x6773637e, 0x69b15475, 0x95576767, 0x02616755,
+ 0x66640102, 0x00021421, 0x00020000, 0xf0326000,
+ 0x2543e520, 0x20002242, 0x41152645, 0x92740277,
+ 0x1135175d, 0x9bf13427, 0x95523595, 0x05422272,
+ 0x72d66364, 0x73f25003, 0x95576677, 0xfa6233a9,
+ 0x06267000, 0x20727542, 0x27674145, 0x44022045,
+ 0x53151571, 0x9d915525, 0xf55243d7, 0x00422122,
+ 0x47351550, 0x55d23425, 0xb5522277, 0xf5222266,
+ 0x0b935020, 0x12022024, 0x277437e7, 0x42742277,
+ 0x61736756, 0x69975775, 0xb27235e1, 0x70646725,
+ 0x32666370, 0x75945625, 0xf2763241, 0xf0326639
+};
+
+void ren_begin(Renderer* r, unsigned* t, int w, int h) {
+ r->t = t;
+ r->w = w;
+ r->h = h;
+ r->clip[0] = 0;
+ r->clip[1] = 0;
+ r->clip[2] = w;
+ r->clip[3] = h;
+}
+
+void ren_end(Renderer* r) {
+ (void)r;
+}
+
+void ren_clear(Renderer* r) {
+ int e = r->w * r->h >> 5, i;
+ for (i = 0; i < e; i++)
+ r->t[i] = 0;
+}
+
+void ren_char(Renderer* r, int x, int y, char ch) {
+ Rect re, su;
+ int i, j, sx, sy, ex, ey;
+ re.x = x;
+ re.y = y;
+ re.w = 4;
+ re.h = 4;
+ su.x = (ch - ' ') * 4;
+ su.y = 0;
+ su.w = 4;
+ su.h = 4;
+ rect_clipsr(&re, &su, r->clip);
+ ex = re.x + re.w;
+ ey = re.y + re.h;
+ for (j = re.y, sy = su.y; j < ey; j++, sy++) {
+ for (i = re.x, sx = su.x; i < ex; i++, sx++) {
+ int di = (i + j * r->w);
+ int si = (sx + sy * smol_font_w);
+ int bit = (1 << (si & 0x1f));
+ bit &= smol_font_data[si >> 5];
+ r->t[di >> 5] ^= (1 << (di & 0x1f)) * (bit != 0);
+ }
+ }
+}
+
+void ren_text(Renderer* r, int x, int y, const char* t) {
+ for (; *t; t++, x += 4)
+ ren_char(r, x, y, *t);
+}
+
+void ren_map(
+ Renderer* r,
+ int x,
+ int y,
+ const Rect* re,
+ const Bitmap* bm
+) {
+ Rect rect, su = *re;
+ const unsigned* data = (const unsigned*)&bm[1];
+ int i, j, sx, sy, ex, ey;
+ rect.x = x;
+ rect.y = y;
+ rect.w = re->w;
+ rect.h = re->h;
+ rect_clipsr(&rect, &su, r->clip);
+ ex = rect.x + rect.w;
+ ey = rect.y + rect.h;
+ for (j = rect.y, sy = su.y; j < ey; j++, sy++) {
+ for (i = rect.x, sx = su.x; i < ex; i++, sx++) {
+ int di = (i + j * r->w);
+ int si = (sx + sy * bm->w);
+ int bit = (1 << (si & 0x1f));
+ bit &= data[si >> 5];
+ r->t[di >> 5] ^= (1 << (di & 0x1f)) * (bit != 0);
+ }
+ }
+}
+
diff --git a/render.h b/render.h
new file mode 100644
index 0000000..01391c5
--- /dev/null
+++ b/render.h
@@ -0,0 +1,29 @@
+#ifndef render_h
+#define render_h
+
+struct Rect;
+
+typedef struct Bitmap {
+ short w, h;
+} Bitmap;
+
+typedef struct {
+ unsigned* t;
+ int w, h;
+ int clip[4];
+} Renderer;
+
+void ren_begin(Renderer* r, unsigned* t, int w, int h);
+void ren_end(Renderer* r);
+void ren_clear(Renderer* r);
+void ren_char(Renderer* r, int x, int y, char ch);
+void ren_text(Renderer* r, int x, int y, const char* t);
+void ren_map(
+ Renderer* r,
+ int x,
+ int y,
+ const struct Rect* re,
+ const Bitmap* bm
+);
+
+#endif