From 259237fae792e8f19d9b7920b2354f75bc1789e2 Mon Sep 17 00:00:00 2001 From: quou Date: Mon, 23 Sep 2024 21:11:29 +1000 Subject: initial commit, basic platform and rendering code. --- .gitignore | 9 ++ 1bitjam.c | 50 ++++++ Makefile | 64 ++++++++ asset.c | 10 ++ asset.h | 11 ++ config.h | 15 ++ convimg.c | 98 +++++++++++ error.h | 11 ++ intermediate/guy.bmp | Bin 0 -> 394 bytes intermediate/hello.bmp | Bin 0 -> 4234 bytes maths.c | 36 +++++ maths.h | 22 +++ memory.c | 225 ++++++++++++++++++++++++++ memory.h | 56 +++++++ packassets.c | 126 +++++++++++++++ plat.c | 429 +++++++++++++++++++++++++++++++++++++++++++++++++ plat.h | 76 +++++++++ rect.c | 85 ++++++++++ rect.h | 13 ++ render.c | 97 +++++++++++ render.h | 29 ++++ 21 files changed, 1462 insertions(+) create mode 100644 .gitignore create mode 100644 1bitjam.c create mode 100644 Makefile create mode 100644 asset.c create mode 100644 asset.h create mode 100644 config.h create mode 100644 convimg.c create mode 100644 error.h create mode 100644 intermediate/guy.bmp create mode 100644 intermediate/hello.bmp create mode 100644 maths.c create mode 100644 maths.h create mode 100644 memory.c create mode 100644 memory.h create mode 100644 packassets.c create mode 100644 plat.c create mode 100644 plat.h create mode 100644 rect.c create mode 100644 rect.h create mode 100644 render.c create mode 100644 render.h 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 +#include +#include + +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 Binary files /dev/null and b/intermediate/guy.bmp differ diff --git a/intermediate/hello.bmp b/intermediate/hello.bmp new file mode 100644 index 0000000..433cd18 Binary files /dev/null and b/intermediate/hello.bmp 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 +#include + +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 +#include + +#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 +#include +#include +#include +#include +#include + +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 +#include + +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 -- cgit v1.2.3-54-g00ecf