From 55ac075e4d8a1359f544027be5be49bd0908de5b Mon Sep 17 00:00:00 2001 From: veclavtlica Date: Thu, 29 Oct 2026 22:12:47 +0300 Subject: [PATCH] working dos vga writes! --- .gitignore | 1 + Common/Snd/bitcrsh.h | 29 ++++++++++ Common/Snd/oscs.h | 91 ++++++++++++++++++++++++++++++ Common/Std/libc.c | 3 +- Common/Std/nolibc.c | 16 ++++++ Common/Std/stub.c | 14 +---- Common/bnch.h | 54 ++++++++++++++++++ Common/cfg.h | 5 ++ Common/fix.c | 34 ----------- Common/fix.h | 10 ++-- Common/std.h | 32 ++++++----- Common/str.h | 2 +- Player/Audio/alsa.c | 3 + Player/Display/dos.c | 53 ++++++++--------- Player/Display/x11.c | 38 +++++++------ Player/Main/dos.s | 2 - Player/Socket/socket.h | 0 Player/Socket/unix.c | 26 --------- Player/docs.txt | 11 ++-- Tools/Tracker/main.c | 31 ++++++++++ Tools/Tracker/test.raw | Bin 0 -> 441000 bytes Tools/compress_img_test.py | 100 +++++++++++++++++++++++++++++++++ Tools/compress_snd_test.py | 44 +++++++++++++++ {Helpers => Tools}/fixedgen.py | 0 Tools/img-index.sh | 4 ++ 25 files changed, 454 insertions(+), 149 deletions(-) create mode 100644 Common/Snd/bitcrsh.h create mode 100644 Common/Snd/oscs.h create mode 100644 Common/bnch.h create mode 100644 Player/Audio/alsa.c delete mode 100644 Player/Socket/socket.h delete mode 100644 Player/Socket/unix.c create mode 100644 Tools/Tracker/main.c create mode 100644 Tools/Tracker/test.raw create mode 100755 Tools/compress_img_test.py create mode 100755 Tools/compress_snd_test.py rename {Helpers => Tools}/fixedgen.py (100%) create mode 100755 Tools/img-index.sh diff --git a/.gitignore b/.gitignore index 6157417..56f681c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,6 @@ Player/player Player/player.wasm Player/Cosmo Master/master +Tools/assets Player/Out/Cosmo/player* diff --git a/Common/Snd/bitcrsh.h b/Common/Snd/bitcrsh.h new file mode 100644 index 0000000..32f1b8f --- /dev/null +++ b/Common/Snd/bitcrsh.h @@ -0,0 +1,29 @@ +/* + Simple fixed point bitcrash. + https://github.com/schollz/fpfx +*/ + +#ifndef COM_SND_BITCRSH_H +#define COM_SND_BITCRSH_H + +#include "../fix.h" +#include + +struct com_snd_bitcrsh { + uint8_t bits; /* How much to trim when triggered. */ + uint8_t reduce; /* Every Nth sample frame. */ +}; + +static void com_snd_bitcrsh_process(Bitcrush *bitcrush, int32_t *buf, + unsigned int nr_samples) { + for (int i = 0; i < nr_samples; i++) { + if (i % bitcrush->reduce == 0) { + // bitcrush fixedpoint + buf[i] = buf[i] >> (16 - bitcrush->bits) << (16 - bitcrush->bits); + } else { + buf[i] = buf[i - 1]; + } + } +} + +#endif diff --git a/Common/Snd/oscs.h b/Common/Snd/oscs.h new file mode 100644 index 0000000..f8578b4 --- /dev/null +++ b/Common/Snd/oscs.h @@ -0,0 +1,91 @@ +/* + Dinky fixed point oscillators. + https://git.poto.cafe/veclavtalica/stfu/src/branch/master/Source/oscillators.c +*/ + +#ifndef COM_SND_OSCS_H +#define COM_SND_OSCS_H + +#include "../cfg.h" +#include "../fix.h" +#include "../std.h" +#include + +/* Intended to be executed offline with values then embedded in the binary. */ +struct com_snd_swave { + uint16_t f, s, c; +}; + +static struct com_snd_swave +com_snd_swave_init(com_fix_t frequency, uint16_t phase, uint16_t amplitude) { + com_std_assert(frequency > 0); + struct com_snd_swave r; + r.f = 2.f * sinf((float)M_PI * frequency / STFU_AUDIO_FRAME_RATE); + r.s = amplitude * sinf(phase); + r.c = amplitude * cosf(phase); + return r; +} + +static inline int16_t com_snd_swave_pump(struct com_snd_swave *wave) { + int16_t const f = com_fix_sin(com_fix_mul( + wave->phase, com_fix_div(com_fix_mul(wave->frequency, COM_FIX_PI2), + COM_CFG_SNDFRMRATE))); + wave->phase += 1; + return f; +} + +/* Implemented over sinewave */ +// struct com_snd_oscs_sqrtwave { +// struct com_snd_swave w; +// union { +// float f; +// uint32_t u; +// } v; +// } com_snd_oscs_init_sqrtwave(float frequency, float phase, float amplitude) { +// struct com_snd_oscs_sqrtwave r; +// union { +// float f; +// uint32_t u; +// } v, a; +// r.w = com_snd_oscs_init_sinewave(frequency, phase, 1.f); +// v.f = r.w.s; +// a.f = amplitude; +// r.v.u = (a.u & 0x7fffffff) | (v.u & 0x80000000); +// return r; +// } + +// /* Use floating point bit representation to infer sign, all other bits are +// set +// * to amplitude */ +// struct com_snd_oscs_sqrtwave +// com_snd_oscs_pump_sqrtwave(struct com_snd_oscs_sqrtwave wave) { +// union { +// float f; +// uint32_t u; +// } v; +// com_snd_oscs_pump_sinewave(wave.w); +// v.f = wave.w.s; +// wave.v.u = (wave.v.u & 0x7fffffff) | (v.u & 0x80000000); +// return wave; +// } + +// struct com_snd_oscs_sawtooth { +// float v, a, i; +// } com_snd_oscs_init_sawtooth(float frequency, float phase, float amplitude) { +// struct com_snd_oscs_sawtooth r; +// r.v = sinf(phase) * amplitude; +// r.a = amplitude; +// r.i = 2.f * frequency / STFU_AUDIO_FRAME_RATE * amplitude; +// return r; +// } + +// /* Simple amplitude overflow check with truncation */ +// struct com_snd_oscs_sawtooth +// com_snd_oscs_pump_sawtooth(struct com_snd_oscs_sawtooth wave) { +// wave.v += wave.i; +// if (wave.v > wave.a) +// wave.v -= wave.a * 2.f; +// return wave; +// } + +#endif diff --git a/Common/Std/libc.c b/Common/Std/libc.c index abfc6b0..565ebcc 100644 --- a/Common/Std/libc.c +++ b/Common/Std/libc.c @@ -1,5 +1,4 @@ #include "../std.h" -#include "any.c" #include #include #include @@ -10,6 +9,8 @@ #include #include +#include "any.c" + void *com_std_alloc(void *ptr, size_t size) { return size != 0 ? realloc(ptr, size) : (free(ptr), NULL); } diff --git a/Common/Std/nolibc.c b/Common/Std/nolibc.c index 0c87382..d1b93a1 100644 --- a/Common/Std/nolibc.c +++ b/Common/Std/nolibc.c @@ -24,6 +24,8 @@ size_t strlen(char const *s) { return p - s; } +#if COM_DEF_CPU == COM_DEF_CPU_AMD64 || COM_DEF_CPU == COM_DEF_CPU_X86 + double sin(double v) { double r; __asm__ __volatile__("fsin" : "=t"(r) : "0"(v)); @@ -49,3 +51,17 @@ double sqrt(double v) { __asm__ volatile("sqrtss %1, %0" : "=x"(r) : "x"(v)); return r; } + +#else + +double sin(double v) { return __builtin_sin(v); } + +double cos(double v) { return __builtin_cos(v); } + +void sincos(double v, double *s, double *c) { + return __builtin_sincos(v, s, c); +} + +double sqrt(double v) { return __builtin_sqrt(v); } + +#endif diff --git a/Common/Std/stub.c b/Common/Std/stub.c index 6f5443f..d138370 100644 --- a/Common/Std/stub.c +++ b/Common/Std/stub.c @@ -1,23 +1,13 @@ #include "../std.h" #include "any.c" +#include "nolibc.c" #include #include void *com_std_alloc(void *ptr, size_t size) { return NULL; } -double com_std_sqrt(double v) { return __builtin_sqrt(v); } -double com_std_fabs(double v) { return __builtin_fabs(v); } -#if COM_DEF_COMPILER == COM_DEF_COMPILER_CLANG -double com_std_sin(double v) { return __builtin_sin(v); } -double com_std_cos(double v) { return __builtin_cos(v); } -#else -/* TODO: Use fsin/fcos, no matter how slow. */ -double com_std_sin(double v) { return 0; } -double com_std_cos(double v) { return 0; } -#endif - com_std_fd com_std_open(char const *path, uint32_t flags) { return NULL; } bool com_std_write(com_std_fd fd, void const *data, uint32_t bytes) { return false; @@ -28,7 +18,7 @@ com_std_fd com_std_socket(bool isnetwork) { return NULL; } bool com_std_connect(com_std_fd fd, bool isnetwork, char const *address) { return false; } -void com_std_exit(uint32_t status) { +void com_std_exit(int32_t status) { __builtin_trap(); while (1) { } diff --git a/Common/bnch.h b/Common/bnch.h new file mode 100644 index 0000000..dfcfa22 --- /dev/null +++ b/Common/bnch.h @@ -0,0 +1,54 @@ +/* + Centralized benchmark suite. +*/ + +#include "fix.h" +#include "std.h" +#include "time.h" +#include + +#define TEST(p_e) \ + do { \ + if (!(p_e)) { \ + com_std_print('c', COM_DEF_STRLIT(p_e) " failed!"); \ + anyfail = true; \ + } \ + } while (0) + +static void com_bnch_fix(void) { + uint64_t start = com_timer_count_ns(); + for (int i = 5000000; i--;) { + com_fix_t sqrt = com_fix_sqrt(i); + com_def_profile_sink(sqrt); + } + com_timer_profile(start, "fixed: sqrt"); + + start = com_timer_count_ns(); + for (int i = 50000000; i--;) { + com_fix_t sin = com_fix_sin(i); + com_def_profile_sink(sin); + } + com_timer_profile(start, "fixed: sin"); + + start = com_timer_count_ns(); + for (int i = 50000000; i--;) { + com_fix_t cos = com_fix_cos(i); + com_def_profile_sink(cos); + } + com_timer_profile(start, "fixed: cos"); + + start = com_timer_count_ns(); + for (int i = 50000000; i--;) { + com_fix_t sin, cos; + com_fix_sincos(i, &sin, &cos); + com_fix_t sinv = sin; + com_fix_t cosv = cos; + com_def_profile_sink(sinv); + com_def_profile_sink(cosv); + } + com_timer_profile(start, "fixed: sincos"); +} + +static void com_bnch_run(void) { com_bnch_fix(); } + +#undef TEST diff --git a/Common/cfg.h b/Common/cfg.h index 6262a24..9284c6b 100644 --- a/Common/cfg.h +++ b/Common/cfg.h @@ -19,4 +19,9 @@ #define COM_CFG_FBSIZE (COM_CFG_WIDTH * COM_CFG_HEIGHT * COM_CFG_COLORS) +#define COM_CFG_SNDFRMRATE 44100 + +/* TODO: Generate file with precompiled note frequencies. */ +#define COM_CFG_A4FIX (440 << COM_FIX_FRACBITS) + #endif diff --git a/Common/fix.c b/Common/fix.c index 200d706..7df26ce 100644 --- a/Common/fix.c +++ b/Common/fix.c @@ -70,37 +70,3 @@ com_fix_t com_fix_sqrt(com_fix_t a) { return final_shift > 0 ? (int32_t)(x << final_shift) : (int32_t)(x >> (-final_shift)); } - -void com_fix_run_bench(void) { - uint64_t start = com_timer_count_ns(); - for (int i = 5000000; i--;) { - com_fix_t sqrt = com_fix_sqrt(i); - com_def_profile_sink(sqrt); - } - com_timer_profile(start, "fixed: sqrt"); - - start = com_timer_count_ns(); - for (int i = 50000000; i--;) { - com_fix_t sin = com_fix_sin(i); - com_def_profile_sink(sin); - } - com_timer_profile(start, "fixed: sin"); - - start = com_timer_count_ns(); - for (int i = 50000000; i--;) { - com_fix_t cos = com_fix_cos(i); - com_def_profile_sink(cos); - } - com_timer_profile(start, "fixed: cos"); - - start = com_timer_count_ns(); - for (int i = 50000000; i--;) { - com_fix_t sin, cos; - com_fix_sincos(i, &sin, &cos); - com_fix_t sinv = sin; - com_fix_t cosv = cos; - com_def_profile_sink(sinv); - com_def_profile_sink(cosv); - } - com_timer_profile(start, "fixed: sincos"); -} diff --git a/Common/fix.h b/Common/fix.h index f380fe2..af7bc82 100644 --- a/Common/fix.h +++ b/Common/fix.h @@ -23,9 +23,6 @@ typedef int32_t com_fix_t; extern const uint16_t com_fix_sin_lut[128]; -void com_fix_run_tests(void); -void com_fix_run_bench(void); - static inline com_fix_t com_fix_add(com_fix_t a, com_fix_t b) { return a + b; } static inline com_fix_t com_fix_sub(com_fix_t a, com_fix_t b) { return a - b; } @@ -64,6 +61,7 @@ static inline com_fix_t com_fix_floor(com_fix_t const a) { com_fix_t com_fix_sqrt(com_fix_t a); /* Approximated using LUT. */ +/* Integer part only houses sign. */ /* TODO: test whether linearly interpolated version could be default. */ /* things like camera movement can be jarring if done without.*/ /* https://namoseley.wordpress.com/2015/07/26/sincos-generation-using-table-lookup-and-iterpolation/ @@ -90,9 +88,11 @@ static inline com_fix_t com_fix_sin(com_fix_t q) { } } +static inline com_fix_t com_fix_sin_from_lut(uint8_t l) { + return com_fix_sin_lut[l]; +} + /* Implemented over sin, as to share one single LUT. */ -/* Additionally, optimizer probably can collapse some math for combined sincos - * case. */ static inline com_fix_t com_fix_cos(com_fix_t a) { return com_fix_sin(a + COM_FIX_PIHALF); } diff --git a/Common/std.h b/Common/std.h index 2141af0..d4c79c3 100644 --- a/Common/std.h +++ b/Common/std.h @@ -6,6 +6,8 @@ #ifndef COM_STD_H #define COM_STD_H +#include "def.h" + #include #include #include @@ -40,20 +42,6 @@ void com_std_close(com_std_fd fd); /* Note: Freeing is done by passing 0 size to pointer. */ void *com_std_alloc(void *ptr, size_t size); -/* Note: Functions listed below are only available for dev builds. */ -/* Trigonometry functions are for reference testing of our own fixed point - * implementations. Do not use otherwise. */ -#define com_std_assert(p_h) \ - do { \ - if (!(p_h)) { \ - const char msg[] = \ - "Assertion failed at "__FILE__ \ - ":" COM_DEF_STRLIT(__LINE__) ": " COM_DEF_STRLIT(p_h); \ - com_std_print_str(msg, sizeof msg, true); \ - com_std_exit(-1); \ - } \ - } while (0) - /* Basic printing utility that doesn't utilize format string. Instead a vector of arguments is passed and concatenated, based on type. @@ -81,9 +69,25 @@ void com_std_print_(int32_t start, ...); #define com_std_print(...) com_std_print_(__VA_ARGS__, '.') void com_std_print_str(char const *str, uint32_t len, bool newline); +#define COM_STD_PIF ((float)3.14159265358979323846) + double com_std_sqrt(double v); double com_std_fabs(double v); double com_std_sin(double v); double com_std_cos(double v); +/* Note: Functions listed below are only available for dev builds. */ +/* Trigonometry functions are for reference testing of our own fixed point + * implementations. Do not use otherwise. */ +#define com_std_assert(p_h) \ + do { \ + if (!(p_h)) { \ + const char msg[] = \ + "Assertion failed at "__FILE__ \ + ":" COM_DEF_STRLIT(__LINE__) ": " COM_DEF_STRLIT(p_h); \ + com_std_print_str(msg, sizeof msg, true); \ + com_std_exit(-1); \ + } \ + } while (0) + #endif diff --git a/Common/str.h b/Common/str.h index aa51dd0..fbb2adc 100644 --- a/Common/str.h +++ b/Common/str.h @@ -49,7 +49,7 @@ static inline int32_t com_str_ext_find_char(char const *s, char ch) { static inline bool com_str_ext_prefixed(char const *__restrict a, struct com_str const *prefix) { uint32_t n = prefix->len; - char *b = prefix->data; + char const *b = prefix->data; while (n && *a && (*a == *b)) { ++a; ++b; diff --git a/Player/Audio/alsa.c b/Player/Audio/alsa.c new file mode 100644 index 0000000..0a24cda --- /dev/null +++ b/Player/Audio/alsa.c @@ -0,0 +1,3 @@ +/* + https://gist.github.com/takaswie/d8a9553c230971d974a3dfc759b2ff67 +*/ diff --git a/Player/Display/dos.c b/Player/Display/dos.c index ca1a112..846084a 100644 --- a/Player/Display/dos.c +++ b/Player/Display/dos.c @@ -1,3 +1,4 @@ +#include "../../Common/def.h" #include "display.h" #include #include @@ -259,6 +260,17 @@ static inline bool set_segment_rights(uint16_t selector, bool read, return !carry_clear; } +static inline intptr_t get_segment_base_address(uint16_t selector) { + bool carry_clear; + uint16_t high, low; + __asm__ volatile("int $0x31" + : "=@ccnc"(carry_clear), "=c"(high), "=d"(low) + : "a"(0x0006), "b"(selector) + : "memory", "cc"); + // return carry_clear ? 0 : (((intptr_t)high << 16) | (intptr_t)low); + return ((intptr_t)high << 16) | (intptr_t)low; +} + static inline void use_segment(uint16_t selector) { __asm__ volatile("" : "=S"(selector)::); } @@ -271,40 +283,19 @@ static inline void print_to_console(char *const str) { static uint32_t test = 0x1420124; -// extern int plr_display_dos_main(int argc, char *argv[]) { extern int plr_display_dos_main(void) { - print_to_console("Fuck you stupid shit"); - return -1; - - uint16_t base = alloc_ldt_descriptor(); - if (!base) { - print_to_console("Cannot allocate LDT descriptor"); + /* Fat DS trick, see __djgpp_nearptr_enable() */ + unsigned short ds_selector; + asm("movw %%ds, %0" : "=r"(ds_selector)); + set_segment_limit(ds_selector, 0xFFFFFFFF); + intptr_t baseaddr = get_segment_base_address(ds_selector); + if (baseaddr == 0) return -1; - } - if (!set_segment_base_address(base, 0xA0000)) { - print_to_console("Cannot set LDT base address"); - return -1; - } - - if (!set_segment_limit(base, 0x10000)) { - print_to_console("Cannot set LDT limit"); - return -1; - } - - use_segment(base); - - // char *volatile buf = (void *)(intptr_t)0xA0000; // VGA memory address - // char *buf = map_physical_address(0xA0000, 0x10000); // VGA memory - char *buf = map_physical_address(0xA0000, 0x10000); // VGA memory - // address - if (!buf) - return -1; - int x, y; - - // if (!buf) - // return -1; + unsigned char *volatile buf = + (unsigned char *)((intptr_t)0xA0000 - baseaddr); // VGA memory address + /* TODO: Make VESA work for sweet sweet 640x480. */ // unsigned char saved_mode = get_video_mode(); // Save original mode // set_vesa_video_mode(MODE_VESA_640_480_8); // Set our VGA mode set_video_mode(MODE_VGA_320_200_8); // Set our VGA mode @@ -327,6 +318,7 @@ extern int plr_display_dos_main(void) { // wait_for_key(); // set_video_mode(saved_mode); // Restore original mode + int x, y; while (1) { for (y = 0; y < HEIGHT; y++) { for (x = 0; x < WIDTH; x++) { @@ -339,7 +331,6 @@ extern int plr_display_dos_main(void) { } } // writetest(); - __asm__ volatile("int $0x16" : : "a"(0x00) : "memory", "cc"); } // dpmi_regs = (struct dpmi_regs){.eax = 0x00004C00}; diff --git a/Player/Display/x11.c b/Player/Display/x11.c index 044490e..8484ff0 100644 --- a/Player/Display/x11.c +++ b/Player/Display/x11.c @@ -126,9 +126,11 @@ static bool plr_x11_handshake(com_std_fd xfd, if (!com_std_write(xfd, AUTH_ENTRY_MAGIC_COOKIE, sizeof AUTH_ENTRY_MAGIC_COOKIE - 1)) return false; - /* Use 2 bytes back for padding. */ - if (!com_std_write(xfd, &auth_entry.auth_data->data[-2], - auth_entry.auth_data->len + 2)) + /* 2 bytes padding after magic cookie. */ + if (!com_std_write(xfd, &request, 2)) + return false; + if (!com_std_write(xfd, auth_entry.auth_data->data, + auth_entry.auth_data->len)) return false; struct { @@ -293,8 +295,8 @@ static uint32_t plr_x11_create_graphical_context(com_std_fd xfd) { static uint32_t plr_x11_create_window(com_std_fd xfd) { /* TODO: Use the ones provided in headers ? */ -#define FLAG_WIN_BG_PIXEL 2 -#define FLAG_WIN_EVENT 0x800 +#define FLAG_WIN_BG_PIXEL 0x0002 +#define FLAG_WIN_EVENT 0x0800 #define EVENT_FLAG_EXPOSURE 0x8000 #define EVENT_FLAG_KEY_PRESS 0x1 #define EVENT_FLAG_KEY_RELEASE 0x2 @@ -331,7 +333,7 @@ static uint32_t plr_x11_create_window(com_std_fd xfd) { .class = 1, .root_visual_id = plr_x11_root_screen.root_visual_id, .bitmask = FLAG_WIN_BG_PIXEL | FLAG_WIN_EVENT, - .value1 = 0x00000000, + .value1 = 0xff000000, .value2 = EVENT_FLAG_EXPOSURE | EVENT_FLAG_BUTTON_RELEASE | EVENT_FLAG_BUTTON_PRESS | EVENT_FLAG_KEY_PRESS | EVENT_FLAG_KEY_RELEASE, @@ -419,6 +421,10 @@ static uint8_t plr_x11_query_extension(com_std_fd xfd, com_str const *ext) { static bool plr_x11_enable_big_requests(com_std_fd xfd) { static com_str bigreqext = com_str_lit("BIG-REQUESTS"); uint8_t const opcode = plr_x11_query_extension(xfd, &bigreqext); + if (!opcode) { + com_std_print('c', "Big request can't accommodate for framebuffer."); + return false; + } /* Request the thing. */ struct { @@ -439,8 +445,8 @@ static bool plr_x11_enable_big_requests(com_std_fd xfd) { plr_x11_poll_reply(xfd, seq, &response); uint32_t const limit = *(uint32_t *)response.reply.pad; - com_std_print('c', "Request limit: ", 'u', limit); - if (limit < COM_CFG_FBSIZE) { + com_std_print('c', "X11 request limit: ", 'u', limit); + if (limit < COM_CFG_FBSIZE / 4) { com_std_print('c', "Big request can't accommodate for framebuffer."); return false; } @@ -449,7 +455,7 @@ static bool plr_x11_enable_big_requests(com_std_fd xfd) { } static bool plr_x11_put_framebuffer(com_std_fd xfd, uint32_t gc_id, - uint32_t pixmap_id, + uint32_t drawable_id, uint8_t pixels[COM_CFG_FBSIZE]) { uint32_t const padded = com_bits_nalign(COM_CFG_FBSIZE, 4); uint32_t const seq = plr_x11_next_seqid(); @@ -472,7 +478,7 @@ static bool plr_x11_put_framebuffer(com_std_fd xfd, uint32_t gc_id, .opcode = 72, .format = 2, // ZPixmap .request_length = sizeof request / 4 + padded / 4, - .drawable_id = pixmap_id, + .drawable_id = drawable_id, .gc_id = gc_id, .width = COM_CFG_WIDTH, .height = COM_CFG_HEIGHT, @@ -600,11 +606,11 @@ extern int plr_display_x11_main(int argc, char const *const *argv) { return -1; } - uint32_t const fb = plr_x11_create_pixmap(xfd, win); - if (!fb) { - com_std_print('c', "Failure to initialize X11"); - return -1; - } + // uint32_t const fb = plr_x11_create_pixmap(xfd, win); + // if (!fb) { + // com_std_print('c', "Failure to initialize X11"); + // return -1; + // } // Display *display = XOpenDisplay(NULL); // if (NULL == display) { @@ -704,7 +710,7 @@ extern int plr_display_x11_main(int argc, char const *const *argv) { uint8_t *fbm = plr_draw_get_pixel_buffer(); plr_draw_screen(fbm); - if (!plr_x11_put_framebuffer(xfd, gc, fb, fbm)) { + if (!plr_x11_put_framebuffer(xfd, gc, win, fbm)) { com_std_print('c', "Failure to drive X11"); return -1; } diff --git a/Player/Main/dos.s b/Player/Main/dos.s index d6c7ed3..34d837a 100644 --- a/Player/Main/dos.s +++ b/Player/Main/dos.s @@ -1,8 +1,6 @@ .section .data .align 4 # TODO: Make sure it's placed under 1 MiB. -# The DPMI Register Structure (50 bytes total) -# This holds the register states injected into Real Mode. .section .text .global _start diff --git a/Player/Socket/socket.h b/Player/Socket/socket.h deleted file mode 100644 index e69de29..0000000 diff --git a/Player/Socket/unix.c b/Player/Socket/unix.c deleted file mode 100644 index 6019422..0000000 --- a/Player/Socket/unix.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - Inter-process communicating socket, useful for local testing and admin - playing. -*/ - -#include -#include -#include - -#define MAXBUFFSIZE 4194304 /* 4.0MiB */ - -int plr_socket_unix_try_connect(const char *filepath) { - int sock = 0; - - if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - perror("Error creating client UNIX socket: "); - return -1; - } - - struct sockaddr_un remote = {0}; - remote.sun_family = AF_UNIX; - strcpy(remote.sun_path, filepath); - int const remote_len = SUN_LEN(&remote); - - return 0; -} diff --git a/Player/docs.txt b/Player/docs.txt index d11f84d..0cb8b99 100644 --- a/Player/docs.txt +++ b/Player/docs.txt @@ -6,29 +6,26 @@ We do not use abstractions, as the simplicity of the required set of features sh ---- Restrictions ---- * Fixed sized paletted frambuffer, defaulted to 640x480. * Deterministic logic via Q15.16 fixed point numbers. -* .gam image format, derived from .gif. RLE and Delta Frame Optimiztion is added, palette is predefined. +* .gam image format, derived from .gif. Palette is globally predefined. * Textures are 32x32 pixels. * 30 FPS display rate. * Fonts are in ASCII bitmaps. Tile slicing commands are issued to render them in. -* Audio samples are in our own .sam format (s8 frame delta + lzw). +* Audio samples are in our own .sam format (s8 + ulaw + lzw). * Music pieces are written in our own simple .tam tracker format, using the same .sam sample db. * Connections are over TCP and their supersets (like WebSocket). -* Textbox provides another client-controlled view, with history. * Content usage has to be predefined, to allow preloading and compilation. ---- Building ---- ==== DOS ==== -Open Watcom 2.0 32bit toolchain is used under Linux host. -Place a copy of DOS4GW.EXE alongside the executable. -Expect to need to configure $INCLUDE and $LIB variables. +Nothing is needed. Just compile. ---- .sam format ---- Thin sample compressing scheme tailored for use with .tam tracker. Leading and tailing silence is stored as number of frames. Sample data is 1 channel only, but panning can be expressed via graph. -Sample bit format is signed 8 bit delta, first frame is delta against 0. Clamping is allowed. +Sample bit format is signed 8 bit delta. Clamping is allowed. Generic LZW compression is applied as the last step. diff --git a/Tools/Tracker/main.c b/Tools/Tracker/main.c new file mode 100644 index 0000000..c1b2f09 --- /dev/null +++ b/Tools/Tracker/main.c @@ -0,0 +1,31 @@ +/* + Tracker program to create awesome music! + Is supposed to only work on Linux, as it's where the development happens. +*/ + +#include "../../Common/Snd/oscs.h" +#include "../../Common/Std/libc.c" +#include "../../Common/Timer/posix.c" +#include "../../Common/fix.c" + +#include +#include + +int main(int argc, char **argv) { + struct com_snd_swave swave = com_snd_swave_init(COM_CFG_A4FIX, 0, UINT16_MAX); + + FILE *f = fopen("test.raw", "w"); + if (!f) { + printf("Can't open file %s\n", "test.raw"); + return -1; + } + + /* Generate 5 seconds of sinwave. */ + int16_t frames[COM_CFG_SNDFRMRATE * 5]; + for (int i = 0; i < COM_CFG_SNDFRMRATE * 5; ++i) + frames[i] = com_snd_swave_pump(&swave); + + fwrite(frames, 2, COM_CFG_SNDFRMRATE * 5, f); + + return 0; +} diff --git a/Tools/Tracker/test.raw b/Tools/Tracker/test.raw new file mode 100644 index 0000000000000000000000000000000000000000..87a26f22f3cdf980a70752eea5ea3d55d4556ac1 GIT binary patch literal 441000 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 L6OfbtHaYn}UX(r` literal 0 HcmV?d00001 diff --git a/Tools/compress_img_test.py b/Tools/compress_img_test.py new file mode 100755 index 0000000..82fa01e --- /dev/null +++ b/Tools/compress_img_test.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# Compression testing for image data for expected cases of ours. + +# TODO: Implement our own CLI tool that uses our LZW implementation, as it differs. + +import imageio.v3 as iio +import ncompress +import numpy as np +import sys + +uncompressed = iio.imread(sys.argv[1]) +compressed = ncompress.compress(uncompressed.tobytes()) +print("Uncompressed:", uncompressed.nbytes) +print("Compressed:", len(compressed), len(compressed) / uncompressed.nbytes) + +with open(sys.argv[1] + "-indexed.raw", "rb") as file: + raw_bytes = file.read() +indexmapping = {} +indexset = set(raw_bytes) +for idx, value in enumerate(indexset): + indexmapping[value] = idx +print(indexmapping) +indexmap = np.zeros((uncompressed.shape[0] * uncompressed.shape[1], 1), dtype=np.uint8) +for y in range(uncompressed.shape[0]): + for x in range(uncompressed.shape[1]): + indexmap[y * uncompressed.shape[0] + x] = indexmapping[raw_bytes[y * uncompressed.shape[0] + x]] +indexedcompressed = ncompress.compress(indexmap.tobytes()) +print("Indexed:", len(indexedcompressed), len(indexedcompressed) / len(raw_bytes)) + +# https://arxiv.org/html/2006.02639v2 +def pixel_art_predictive_filter(image_channel): + """ + Applies a spatial prediction filter to a 2D image matrix + to prepare it for lossless compression. + """ + rows, cols = image_channel.shape + residual_map = np.zeros((rows, cols), dtype=np.int16) + + for r in range(rows): + for c in range(cols): + # Handle edge cases (boundaries where neighbors don't exist) + if r == 0 and c == 0: + pred = 0 + elif r == 0: + pred = image_channel[r, c-1] # Only Left neighbor exists + elif c == 0: + pred = image_channel[r-1, c] # Only Top neighbor exists + else: + # Core DSP Neighborhood definition + A = image_channel[r, c-1] # Left + B = image_channel[r-1, c] # Top + C = image_channel[r-1, c-1] # Top-Left + + # Median Edge Detector logic + if C >= max(A, B): + pred = min(A, B) + elif C <= min(A, B): + pred = max(A, B) + else: + pred = int(A) + int(B) - int(C) + + # Map the actual value to the residual difference + residual_map[r, c] = int(image_channel[r, c]) - int(pred) + + return residual_map + +predictivefilter = pixel_art_predictive_filter(indexmap) +predictivefiltercompressed = ncompress.compress(predictivefilter.tobytes()) +print("Predictive filter:", len(predictivefiltercompressed), len(predictivefiltercompressed) / len(raw_bytes)) + +def encode_zorder_2d(x: int, y: int) -> int: + z = 0 + for i in range(16): + z |= ((x & (1 << i)) << i) | ((y & (1 << i)) << (i + 1)) + return z + +zordered = np.zeros((uncompressed.shape[0] * uncompressed.shape[1], 1), dtype=np.uint8) +for y in range(uncompressed.shape[0]): + for x in range(uncompressed.shape[1]): + idx = encode_zorder_2d(x, y) + zordered[idx] = uncompressed[y, x] + +zcompressed = ncompress.compress(zordered.tobytes()) +print("Zordered:", len(zcompressed), len(zcompressed) / uncompressed.nbytes) + +delta = np.zeros((uncompressed.shape[0] * uncompressed.shape[1], 1), dtype=np.int8) +for y in range(uncompressed.shape[0]): + for x in range(uncompressed.shape[1]): + delta[y * uncompressed.shape[0] + x] = int(uncompressed[y, x]) - int(uncompressed[y, (x + 1) % uncompressed.shape[0]]) +deltacompressed = ncompress.compress(delta.tobytes()) +print("Delta:", len(deltacompressed), len(deltacompressed) / uncompressed.nbytes) + +deltaz = np.zeros((uncompressed.shape[0] * uncompressed.shape[1], 1), dtype=np.int8) +for y in range(uncompressed.shape[0]): + for x in range(uncompressed.shape[1]): + idx = encode_zorder_2d(x, y) + deltaz[idx] = int(uncompressed[y, x]) - int(uncompressed[y, (x + 1) % uncompressed.shape[0]]) +deltazcompressed = ncompress.compress(deltaz.tobytes()) +print("Deltaz:", len(deltazcompressed), len(deltazcompressed) / uncompressed.nbytes) + diff --git a/Tools/compress_snd_test.py b/Tools/compress_snd_test.py new file mode 100755 index 0000000..f3a1e2f --- /dev/null +++ b/Tools/compress_snd_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# Compression testing for sound sample data for expected cases of ours. + +# TODO: Implement our own CLI tool that uses our LZW implementation, as it differs. + +import ncompress +import sys +import wave +import audioop +import numpy as np +import array + +with wave.open(sys.argv[1], 'rb') as wf: + channels = wf.getnchannels() + samplerate = wf.getframerate() + frames = wf.getnframes() + uncompressed = wf.readframes(frames) + depth = wf.getsampwidth() + +compressed = ncompress.compress(uncompressed) +print("Uncompressed:", len(uncompressed)) +print("Compressed:", len(compressed), len(compressed) / len(uncompressed)) + +delta = bytearray(uncompressed[0].to_bytes(2, signed=False)) +for i in range(len(uncompressed)-1): + delta += (uncompressed[i+1] - uncompressed[i]).to_bytes(2, signed=True) + +dtcompressed = ncompress.compress(bytes(delta)) +print("Delta:", len(dtcompressed), len(dtcompressed) / len(uncompressed)) +assert(len(delta) == len(uncompressed)*2) + +ulaw = audioop.lin2ulaw(uncompressed, depth) +ulawcompressed = ncompress.compress(ulaw) +print("Ulaw:", len(ulawcompressed), len(ulawcompressed) / len(uncompressed)) + +absfold = bytearray() +for i in range(len(uncompressed)): + absfold.append(uncompressed[i] - 128 if uncompressed[i] >= 128 else uncompressed[i]) +absfoldulaw = audioop.lin2ulaw(absfold, depth) +absfoldulawcompressed = ncompress.compress(absfoldulaw) +print("Ulawabs:", len(absfoldulawcompressed), len(absfoldulawcompressed) / len(absfold)) + +with open(sys.argv[1] + ".ulaw", "wb") as file: + file.write(audioop.ulaw2lin(ulaw, depth)) diff --git a/Helpers/fixedgen.py b/Tools/fixedgen.py similarity index 100% rename from Helpers/fixedgen.py rename to Tools/fixedgen.py diff --git a/Tools/img-index.sh b/Tools/img-index.sh new file mode 100755 index 0000000..bb25f65 --- /dev/null +++ b/Tools/img-index.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 + +magick $1 -resize 32x32 +dither -type Palette -depth 8 -colors 16 PNG8:$1-indexed +magick stream -map r -storage-type char $1-indexed $1-indexed.raw