This commit is contained in:
veclavtlica
2026-09-11 02:21:21 +03:00
parent 2e890fb5b7
commit adfae10938
17 changed files with 949 additions and 119 deletions
+30
View File
@@ -0,0 +1,30 @@
#ifndef COM_BITS_H
#define COM_BITS_H
#include "def.h"
#include <assert.h>
#include <stdint.h>
/* How many bits needed to represent a given number */
/* Alternitive semntic: nearest upper power of two */
static inline int com_bits_needed(uint32_t v) {
#ifdef COM_DEF_COMPILE_MODERN
/* Use intrinsic, required for fast sqrt impl */
return v == 0 ? 1 : 32 - __builtin_clz(v);
#else
#endif
if (v == 0)
return 1;
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
#endif
+19
View File
@@ -0,0 +1,19 @@
/*
Compiler definitions helpers, for portability.
*/
#ifndef COM_DEF_H
#define COM_DEF_H
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
defined(__GNUC__) || defined(__clang__)
#define COM_DEF_COMPILE_MODERN 1
#endif
#ifdef COM_DEF_COMPILE_MODERN
#define com_def_alignedas(v_as) _Alignas(v_as)
#else
#define com_def_alignedas(v_as)
#endif
#endif
+71
View File
@@ -0,0 +1,71 @@
#include "fixed.h"
#include "bits.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
// 4-bit LUT (16 entries) for the normalized range [0.5, 2.0)
// It stores the initial guess scaled to Q16.16.
static const uint32_t com_fixed_sqrt_lut[16] = {
46340, 49547, 52521, 55314, 57954, 60464, 62862, 65161,
67373, 69508, 71572, 73572, 75514, 77402, 79240, 81033};
com_fixed_t com_fixed_sqrt(com_fixed_t a) {
// 1. Handle sign and edge cases
assert(a >= 0);
if (a == 0)
return 0;
// 2. Normalize input to the range [0.5, 2.0) to maximize LUT precision
// clz = count leading zeros. On modern hardware, use __builtin_clz
int leading_zeros = com_bits_needed(a);
// Calculate how much we need to shift to place the highest bit properly
// We want the value to land squarely within an optimal window
int shift = (31 - leading_zeros) - COM_FIXED_FRACBITS;
// Normalize shift to always be even so we can cleanly pull out 2^(shift/2)
if (shift & 1)
shift -= 1;
uint32_t normalized_a;
if (shift > 0) {
normalized_a = a >> shift;
} else {
normalized_a = a << (-shift);
}
// 3. LUT Lookup using 4 MSBs of the normalized value
// Extracted index corresponds to the interval [0.5, 2.0)
uint32_t lut_index = (normalized_a >> (COM_FIXED_FRACBITS - 3)) & 0xF;
uint64_t x = com_fixed_sqrt_lut[lut_index];
// 4. Newton-Raphson Iterations: x = 0.5 * (x + normalized_a / x)
// We upscale to 64-bit to prevent intermediate overflow during division
x = (x + ((uint64_t)normalized_a << COM_FIXED_FRACBITS) / x) >>
1; // Iteration 1
x = (x + ((uint64_t)normalized_a << COM_FIXED_FRACBITS) / x) >>
1; // Iteration 2
// 5. Denormalize back to the target scale: result = x * 2^(shift / 2)
int32_t final_shift = shift / 2;
if (final_shift > 0) {
return (int32_t)(x << final_shift);
} else {
return (int32_t)(x >> (-final_shift));
}
}
void com_fixed_print(com_fixed_t a) {
if (a < 0) {
printf("-");
a = -a;
}
int32_t int_part = a >> 16;
int32_t frac_part = a & 0xFFFF;
// Convert fractional 16-bit part to a decimal value (up to 4 decimal places)
uint32_t decimal_val = (frac_part * 10000) >> 16;
printf("%d.%04u\n", int_part, decimal_val);
}
+38
View File
@@ -0,0 +1,38 @@
/*
Doom-inspired Q15.16 format used for most of everything.
https://hackmd.io/9uRB9YbTSBW4Qz_2b8TyDw#Examples-2-DOOM
*/
#ifndef COM_FIXED_H
#define COM_FIXED_H
#include <stdint.h>
#define COM_FIXED_FRACBITS 16
#define COM_FIXED_FRACUNIT (1 << COM_FIXED_FRACBITS)
typedef int32_t com_fixed_t;
static inline com_fixed_t com_fixed_add(com_fixed_t a, com_fixed_t b) {
return a + b;
}
static inline com_fixed_t com_fixed_sub(com_fixed_t a, com_fixed_t b) {
return a - b;
}
static inline com_fixed_t com_fixed_mul(com_fixed_t a, com_fixed_t b) {
return (com_fixed_t)(((int64_t)a * (int64_t)b) >> COM_FIXED_FRACBITS);
}
/* Note: this does not clamp for over/underflow cases */
static inline com_fixed_t com_fixed_div(com_fixed_t a, com_fixed_t b) {
return (com_fixed_t)(((int64_t)a << COM_FIXED_FRACBITS) / (int64_t)b);
}
/* Approximate square root using Newton-Raphson in 2 iterations over small LUT*/
com_fixed_t com_fixed_sqrt(com_fixed_t a);
void com_fixed_print(com_fixed_t a);
#endif
+154
View File
@@ -0,0 +1,154 @@
/*
LZW compression that is required for GIF implementation.
https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
*/
#include "lzw.h"
#include "bits.h"
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/* References: */
/* https://www.w3.org/Graphics/GIF/spec-gif89a.txt */
/* https://www.daubnet.com/en/file-format-gif */
/* After it is reached the table is supposed to be reset */
/* As this is known, the upper boundary of memory used is known */
#define COM_LZW_CODEPOINT_LIMIT 8192
#define COM_TABLE_BIT_WIDTH_LIMIT 12 /* typical GIF impl */
#define COM_LZW_OUTPUT_CAP_GROWTH 256
#define COM_LZW_TABLE_CAP_GROW 128
struct com_lzw_table com_lzw_infer_table(const char *datain, uint32_t sizein) {
struct com_lzw_table_entry *table = NULL;
uint32_t table_init_size = 0;
uint32_t table_size = 0;
uint32_t table_cap = 0;
/* Infer initial table from the data */
for (uint32_t i = 0; i < sizein && table_init_size < 255; i++) {
bool code_found = false;
char code = datain[i];
/* Try searching the code */
for (uint32_t t = 0; t < table_init_size; t++) {
if (table[t].code == code) {
code_found = true;
break;
}
}
/* Append non-existing codepoint */
if (!code_found) {
if (table_size >= table_cap) {
/* TODO: Ref to prev table gets missed, memory leak scenario */
if (!(table = realloc(table, sizeof(struct com_lzw_table_entry) *
(table_cap + COM_LZW_TABLE_CAP_GROW))))
goto ERR_ALLOC_INIT_TABLE;
table_cap += COM_LZW_TABLE_CAP_GROW;
}
table[table_size] = (struct com_lzw_table_entry){.code = code};
table_init_size++;
table_size++;
}
}
return (struct com_lzw_table){
.table = table,
.cap = table_cap,
.size = table_size,
.init_size = table_init_size,
.continuous = false,
};
ERR_ALLOC_INIT_TABLE:
if (table_cap > 0)
free(table);
return (struct com_lzw_table){0};
}
void com_lzw_free_table(struct com_lzw_table *table) {
if (!table->table)
return;
free(table->table);
table->size = 0;
table->cap = 0;
table->init_size = 0;
}
/* For GIFs, color should already be collapsed to indices at this point */
bool com_lzw_compress(const struct com_lzw_table *table, const char *datain,
uint32_t sizein, char **dataout, uint32_t *sizeout) {
assert(datain && dataout && sizeout && sizein > 0);
assert(*dataout == NULL && *sizeout == 0);
/* TODO: Make sure the table is cleared */
/* Encode step */
uint8_t codesize = com_bits_needed(table->init_size) + 1;
assert(codesize < COM_TABLE_BIT_WIDTH_LIMIT);
uint32_t cur_table_idx =
0; /* Head table in which vector we should be looking into */
uint32_t feedback =
0; /* How many bytes feeded from datain to the current string */
char *output = NULL;
uint32_t output_size = 0;
uint32_t output_cap = 0;
uint8_t output_bitshift = 0;
for (uint32_t i = 0; i < sizein; i++) {
char code = datain[i];
if (feedback == 0) {
/* Should not be possible not to find the first code */
for (uint32_t t = 0; t < table->init_size; t++) {
if (table->table[t].code == code) {
cur_table_idx = t;
feedback++;
}
}
} else {
// for (uint32_t v = 0; v < table->table[cur_table_idx].tree_vector_size;
// v++) {
// if (table->table[table->table[cur_table_idx].tree_vector[v]].code ==
// code) {
// cur_table_idx = table->table[cur_table_idx].tree_vector[v];
// feedback++;
// continue;
// }
// }
/* Emit output, drop the string */
assert(cur_table_idx < (1 << codesize));
assert(cur_table_idx < table->size);
if (output_size == output_cap && output_bitshift + codesize > 8) {
/* TODO: catch alloc failure */
output = realloc(output, output_cap + COM_LZW_OUTPUT_CAP_GROWTH);
output_cap += COM_LZW_OUTPUT_CAP_GROWTH;
}
// uint16_t code = cur_table_idx;
// uint8_t bits_to_write = codesize;
// while (bits_to_write > 0) {
// bits_to_write -=
// }
/* Return to prev char as it wasn't processed */
feedback = 0;
i--;
}
}
*sizeout = output_size;
*dataout = output;
return true;
}
+55
View File
@@ -0,0 +1,55 @@
#ifndef COM_LZW_H
#define COM_LZW_H
#include <stdbool.h>
#include <stdint.h>
/* Alphabet table for use in LZW algorithms */
struct com_lzw_table {
/* table_size indexed entry is reserved for clear code */
/* table_size+1 indexed entry is reserved for terminator */
struct com_lzw_table_entry *table;
uint32_t init_size; /* Alphabet size, table clear is done over it */
uint32_t size;
uint32_t cap;
bool continuous; /* If true, first order search is simple indexing, where
codepoint = index, up to table_init_size */
};
struct com_lzw_table_entry {
uint16_t child_chain; /* Start of linked list inside table, or 0 if none */
uint16_t next_child; /* Index of next child, or 0 if none */
char code; /* Single byte, children bytes are appended to it */
};
/* Returns table that is optimized for continuous range of codepoints */
/* Useful for binary compression, such as .GIF and .SAM formats */
struct com_lzw_table com_lzw_continous_table(uint8_t codepoints);
/* Returns table with alphabet inferred from the incoming data */
/* Useful for alphanumeric compression, where not all codepoints are in use */
/* TODO: Sort table by frequency? */
struct com_lzw_table com_lzw_infer_table(const char *datain, uint32_t sizein);
/* Each bit corresponds to a byte value, position-wise */
struct com_lzw_encoded_alphabet {
uint64_t b0;
uint64_t b1;
uint64_t b2;
uint64_t b3;
};
struct com_lzw_encoded_alphabet
com_lzw_encode_alphabet(const struct com_lzw_table *table);
struct com_lzw_table
com_lzw_decode_table(struct com_lzw_encoded_alphabet alphabet);
/* Return table to its original form */
void com_lzw_clear_table(struct com_lzw_table *table);
void com_lzw_free_table(struct com_lzw_table *table);
bool com_lzw_compress(const struct com_lzw_table *table, const char *datain,
uint32_t sizein, char **dataout, uint32_t *sizeout);
#endif
+137
View File
@@ -0,0 +1,137 @@
/*
Fixed point implementation of 4 dimensional matrix.
Some optimizing cases are present, such as reordered matrices and assumed
identity components.
Column-major ordering is assumed unless stated otherwise:
a e k o
b f l p
c g m q
d h n r
In memory: a b c d e f g h ...
*/
#ifndef COM_MAT_H
#define COM_MAT_H
#include "fixed.h"
#include "vec.h"
#include <stdint.h>
#include <stdio.h>
typedef union {
com_fixed_t com_def_alignedas(64) a[4 * 4];
} com_mat_t;
static inline com_mat_t com_mat_identity(void) {
com_mat_t result = {0};
result.a[0 * 4 + 0] = COM_FIXED_FRACUNIT;
result.a[1 * 4 + 1] = COM_FIXED_FRACUNIT;
result.a[2 * 4 + 2] = COM_FIXED_FRACUNIT;
result.a[3 * 4 + 3] = COM_FIXED_FRACUNIT;
return result;
}
/* https://michalpitr.substack.com/p/optimizing-matrix-multiplication */
static inline com_mat_t com_mat_mul(com_mat_t a, com_mat_t b) {
// com_mat_t result = {0};
// for (int c = 0; c < 4; ++c) {
// for (int k = 0; k < 4; ++k) {
// for (int r = 0; r < 4; ++r) {
// result.a[r + c * 4] += com_fixed_mul(a.a[r + k * 4], b.a[k + c * 4]);
// }
// }
// }
com_mat_t result;
for (int c = 0; c < 4; ++c) {
for (int r = 0; r < 4; ++r) {
result.a[r + c * 4] = (((int64_t)a.a[r + 0 * 4] * b.a[0 + c * 4]) +
((int64_t)a.a[r + 1 * 4] * b.a[1 + c * 4]) +
((int64_t)a.a[r + 2 * 4] * b.a[2 + c * 4]) +
((int64_t)a.a[r + 3 * 4] * b.a[3 + c * 4])) >>
COM_FIXED_FRACBITS;
}
}
return result;
}
/* This case might be slightly more optimized, as we can reorder one frequently
* reused matrix, such as VP.
*/
/* Note: second a matrix is assumed to be row-major, reverse of typical. */
static inline com_mat_t com_mat_mul_reodered(com_mat_t a, com_mat_t b) {
com_mat_t result;
for (int c = 0; c < 4; ++c) {
for (int r = 0; r < 4; ++r) {
result.a[r + c * 4] = (((int64_t)a.a[0 + r * 4] * b.a[0 + c * 4]) +
((int64_t)a.a[1 + r * 4] * b.a[1 + c * 4]) +
((int64_t)a.a[2 + r * 4] * b.a[2 + c * 4]) +
((int64_t)a.a[3 + r * 4] * b.a[3 + c * 4])) >>
COM_FIXED_FRACBITS;
}
}
return result;
}
/* Projects vertex position to a screen via reordered row-major MVP matrix,
* which implies division by w in-place */
static inline com_vec_t com_mat_vec_project(com_mat_t a, com_vec_t b) {
com_fixed_t t[4];
com_vec_t result;
for (int c = 0; c < 4; ++c) {
t[c] =
(((int64_t)a.a[c * 4 + 0] * b.s.x) + ((int64_t)a.a[c * 4 + 1] * b.s.y) +
((int64_t)a.a[c * 4 + 2] * b.s.z) + a.a[c * 4 + 3]) >>
COM_FIXED_FRACBITS;
}
/* Creates perspective effect, could be skipped for orthographic */
result.a[0] = com_fixed_div(t[0], t[3]);
result.a[1] = com_fixed_div(t[1], t[3]);
result.a[2] = com_fixed_div(t[2], t[3]);
return result;
}
/* Slightly optimized case of assumed identity scaling, might be useful for MVP
* calculations, if model matrix does not scale. View matrix is always
* unscaled as well.
*/
// static inline com_mat_t com_mat_mul_no_scale(com_mat_t a, com_mat_t b) {
// com_mat_t result;
// /* TODO: calc the rest */
// result.a[0 * 4 + 3] = 0;
// result.a[1 * 4 + 3] = 0;
// result.a[2 * 4 + 3] = 0;
// result.a[3 * 4 + 3] = COM_FIXED_FRACUNIT;
// return result;
// }
/* Reorder between column and row major, it's also called transposing */
static inline com_mat_t com_mat_reoder(com_mat_t a) {
com_mat_t result;
for (int r = 0; r < 4; ++r) {
for (int c = 0; c < 4; ++c) {
result.a[c * 4 + r] = a.a[c + r * 4];
}
}
return result;
}
#endif
+80
View File
@@ -0,0 +1,80 @@
/*
Fixed point implementation of 3 dimensional vectors.
SSE2 extension availability is assumed, making it more viable.
For mul and div we don't use com_fixed functions to make fewer bitshifts.
*/
#ifndef COM_VEC_H
#define COM_VEC_H
#include "def.h"
#include "fixed.h"
#include <stdint.h>
typedef union {
com_fixed_t com_def_alignedas(16) a[3];
com_def_alignedas(16) struct {
com_fixed_t x;
com_fixed_t y;
com_fixed_t z;
} s;
} com_vec_t;
static inline com_vec_t com_vec_add(com_vec_t a, com_vec_t b) {
return (com_vec_t){.s = {.x = com_fixed_add(a.s.x, b.s.x),
.y = com_fixed_add(a.s.y, b.s.y),
.z = com_fixed_add(a.s.z, b.s.z)}};
}
static inline com_vec_t com_vec_sub(com_vec_t a, com_vec_t b) {
return (com_vec_t){.s = {.x = com_fixed_sub(a.s.x, b.s.x),
.y = com_fixed_sub(a.s.y, b.s.y),
.z = com_fixed_sub(a.s.z, b.s.z)}};
}
static inline com_vec_t com_vec_mul(com_vec_t a, com_vec_t b) {
return (com_vec_t){.s = {.x = com_fixed_mul(a.s.x, b.s.x),
.y = com_fixed_mul(a.s.y, b.s.y),
.z = com_fixed_mul(a.s.z, b.s.z)}};
}
/* Note: this does not clamp for over/underflow cases */
static inline com_vec_t com_vec_div(com_vec_t a, com_vec_t b) {
return (com_vec_t){.s = {.x = com_fixed_div(a.s.x, b.s.x),
.y = com_fixed_div(a.s.y, b.s.y),
.z = com_fixed_div(a.s.z, b.s.z)}};
}
/* Scale vector by a fixed point number */
static inline com_vec_t com_vec_scl(com_vec_t a, com_fixed_t b) {
return (com_vec_t){.s = {.x = com_fixed_mul(a.s.x, b),
.y = com_fixed_mul(a.s.y, b),
.z = com_fixed_mul(a.s.z, b)}};
}
/* Shows how much given vectors are correlated in direction to each other */
/* Resulted range depends on input, it's in -1 to 1 for normalized
* input and otherwise is -ab to +ab */
static inline com_fixed_t com_vec_dot(com_vec_t a, com_vec_t b) {
return (((int64_t)a.s.x * b.s.x) + ((int64_t)a.s.y * b.s.y) +
((int64_t)a.s.z * b.s.z)) >>
COM_FIXED_FRACBITS;
}
/* Cross product produces a perpendicular for normalized vectors, or 0 for
* parallel vectors */
static inline com_vec_t com_vec_crs(com_vec_t a, com_vec_t b) {
int64_t const cx = ((int64_t)a.s.y * b.s.z) - ((int64_t)a.s.z - b.s.y);
int64_t const cy = ((int64_t)a.s.z * b.s.x) - ((int64_t)a.s.x - b.s.z);
int64_t const cz = ((int64_t)a.s.x * b.s.y) - ((int64_t)a.s.y - b.s.x);
return (com_vec_t){.s = {.x = (com_fixed_t)(cx >> COM_FIXED_FRACBITS),
.y = (com_fixed_t)(cy >> COM_FIXED_FRACBITS),
.z = (com_fixed_t)(cz >> COM_FIXED_FRACBITS)}};
}
static inline com_vec_t com_vec_nrm(com_vec_t a) {
com_fixed_t const n = com_fixed_sqrt(com_vec_dot(a, a));
return com_vec_scl(a, com_fixed_div(COM_FIXED_FRACUNIT, n));
}
#endif
+5
View File
@@ -1,2 +1,7 @@
#ifndef PLR_DISPLAY_H
#define PLR_DISPLAY_H
extern int plr_display_x11_main(int argc, char *argv[]); extern int plr_display_x11_main(int argc, char *argv[]);
extern int plr_display_dos_main(int argc, char *argv[]);
#endif
+152
View File
@@ -0,0 +1,152 @@
#include "display.h"
#include <stdint.h>
#include <string.h>
#define WIDTH 640
#define HEIGHT 480
#define MODE_VGA_320_200_8 0x13
#define MODE_VESA_640_480_8 0x101
// 8-color VGA palette gradient, lighter shades of lavender to pink
#define COLOR_OFFSET 0x50
#define COLOR_NUM 8
#define COLOR_STEP (HEIGHT / COLOR_NUM)
/* https://wiki.osdev.org/VESA_Video_Modes */
_Packed struct VbeInfoBlock {
char VbeSignature[4]; // == "VESA"
uint16_t VbeVersion; // == 0x0300 for VBE 3.0
uint16_t OemStringPtr[2]; // isa vbeFarPtr
uint8_t Capabilities[4];
uint16_t VideoModePtr[2]; // isa vbeFarPtr
uint16_t TotalMemory; // as # of 64KB blocks
uint8_t Reserved[492];
};
_Packed struct ModeInfoBlock {
uint16_t
attributes; // deprecated, only bit 7 should be of interest to you, and it
// indicates the mode supports a linear frame buffer.
uint8_t window_a; // deprecated
uint8_t window_b; // deprecated
uint16_t granularity; // deprecated; used while calculating bank numbers
uint16_t window_size;
uint16_t segment_a;
uint16_t segment_b;
uint32_t win_func_ptr; // deprecated; used to switch banks from protected mode
// without returning to real mode
uint16_t pitch; // number of bytes per horizontal line
uint16_t width; // width in pixels
uint16_t height; // height in pixels
uint8_t w_char; // unused...
uint8_t y_char; // ...
uint8_t planes;
uint8_t bpp; // bits per pixel in this mode
uint8_t banks; // deprecated; total number of banks in this mode
uint8_t memory_model;
uint8_t bank_size; // deprecated; size of a bank, almost always 64 KB but may
// be 16 KB...
uint8_t image_pages;
uint8_t reserved0;
uint8_t red_mask;
uint8_t red_position;
uint8_t green_mask;
uint8_t green_position;
uint8_t blue_mask;
uint8_t blue_position;
uint8_t reserved_mask;
uint8_t reserved_position;
uint8_t direct_color_attributes;
uint32_t framebuffer; // physical address of the linear frame buffer; write
// here to draw to the screen
uint32_t off_screen_mem_off;
uint16_t off_screen_mem_size; // size of memory in the framebuffer but not
// being displayed on the screen
uint8_t reserved1[206];
};
static char get_video_mode();
#pragma aux get_video_mode = "mov ah, 0x0f" \
"int 0x10" value[al] modify[ah];
static void set_video_mode(unsigned char);
#pragma aux set_video_mode = "mov ah, 0x00" \
"int 0x10" parm[al] modify[ah];
static void set_vesa_video_mode(unsigned short);
#pragma aux set_vesa_video_mode = "mov ax, 0x4f02" \
"int 0x10" parm[bx] modify[ah];
static void wait_for_key();
#pragma aux wait_for_key = "mov ah, 0x00" \
"int 0x16" modify[ah];
static short get_vesa_controller_info(struct VbeInfoBlock *);
#pragma aux get_vesa_controller_info = "mov ax, 0x4f00" \
"int 0x10" parm[es di] value[ax];
static short get_vesa_mode_info(uint16_t mode, struct ModeInfoBlock *);
#pragma aux get_vesa_mode_info = "mov ax, 0x4F01" \
"int 0x10" parm[cx][es di] value[ax];
/* https://wiki.osdev.org/User:Omarrx024/VESA_Tutorial*/
static char *find_display_memory(void) {
struct VbeInfoBlock *ctrl = (struct VbeInfoBlock *)0x2000;
struct ModeInfoBlock *inf = (struct ModeInfoBlock *)0x3000;
uint16_t *modes;
int i;
strncpy(ctrl->VbeSignature, "VBE2", 4);
if (get_vesa_controller_info(ctrl) != 0x004F)
return NULL;
modes = (uint16_t *)(ctrl->VideoModePtr);
for (i = 0; modes[i] != 0xFFFF; ++i) {
if (get_vesa_mode_info(modes[i], inf) != 0x004F)
continue;
// Check if this is a graphics mode with linear frame buffer support
if ((inf->attributes & 0x80) != 0x80)
continue;
// Check if this is a packed pixel or direct color mode
if (inf->memory_model != 4 && inf->memory_model != 6)
continue;
// Check if this is exactly the mode we're looking for
if (WIDTH == inf->width && HEIGHT == inf->height && 256 == inf->pitch)
return (char *)inf->framebuffer;
}
return NULL;
}
extern int plr_display_dos_main(int argc, char *argv[]) {
char far *buf = (char far *)0xA0000; // VGA memory address
int x, y;
unsigned char saved_mode = get_video_mode(); // Save original mode
set_vesa_video_mode(MODE_VESA_640_480_8); // Set our VGA mode
buf = find_display_memory();
/* TODO: Fetch info about the mode, to know the availability */
/* as well as base address in memory */
/* https://wiki.osdev.org/VESA_Video_Modes */
// Now draw. We draw pixel-by-pixel by directly setting the video memory like
// it was an array. We step the color every 25 pixels so that we have 8 bands
// of even height.
for (y = 0; y < HEIGHT; y++) {
for (x = 0; x < WIDTH; x++) {
buf[y * WIDTH + x] = COLOR_OFFSET + y / COLOR_STEP;
}
}
wait_for_key();
set_video_mode(saved_mode); // Restore original mode
return 0;
}
+41 -17
View File
@@ -1,9 +1,14 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/X.h> #include <X11/X.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "../../Common/lzw.h"
#include "../../Common/mat.h"
#include "display.h" #include "display.h"
bool quited = false; bool quited = false;
@@ -12,8 +17,27 @@ bool quited = false;
#define HEIGHT 480 #define HEIGHT 480
// TODO: correct error handling. // TODO: correct error handling.
extern int plr_display_x11_main(int argc, char *argv[]) extern int plr_display_x11_main(int argc, char *argv[]) {
{ char test_string[] = "what is this?";
char *compressed_string = NULL;
uint32_t compressed_string_sz = 0;
struct com_lzw_table table =
com_lzw_infer_table(test_string, sizeof(test_string));
bool test = com_lzw_compress(&table, test_string, sizeof(test_string),
&compressed_string, &compressed_string_sz);
com_lzw_free_table(&table);
assert(test);
com_mat_t m0 = com_mat_identity();
com_mat_t m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
for (int i = 0; i < 16; ++i)
m1.a[i] *= COM_FIXED_FRACUNIT;
com_mat_t t0 = com_mat_mul(m1, m0);
com_mat_t t1 = com_mat_mul_reodered(com_mat_reoder(m1), m0);
com_fixed_print(t0.a[4]);
com_fixed_print(t1.a[4]);
Display *display = XOpenDisplay(NULL); Display *display = XOpenDisplay(NULL);
if (NULL == display) { if (NULL == display) {
fprintf(stderr, "Failed to initialize display"); fprintf(stderr, "Failed to initialize display");
@@ -31,7 +55,8 @@ extern int plr_display_x11_main(int argc, char *argv[])
Visual *visual = DefaultVisual(display, screen); Visual *visual = DefaultVisual(display, screen);
int depth = DefaultDepth(display, screen); int depth = DefaultDepth(display, screen);
Window window = XCreateSimpleWindow(display, root, 0, 0, WIDTH, HEIGHT, 0, 0, 0xffffffff); Window window =
XCreateSimpleWindow(display, root, 0, 0, WIDTH, HEIGHT, 0, 0, 0xffffffff);
if (None == window) { if (None == window) {
fprintf(stderr, "Failed to create window"); fprintf(stderr, "Failed to create window");
XCloseDisplay(display); XCloseDisplay(display);
@@ -39,7 +64,8 @@ extern int plr_display_x11_main(int argc, char *argv[])
} }
XSizeHints *hints = XAllocSizeHints(); XSizeHints *hints = XAllocSizeHints();
if (hints == NULL) return EXIT_FAILURE; if (hints == NULL)
return EXIT_FAILURE;
// Pinning min and max to the same values disables resizing // Pinning min and max to the same values disables resizing
hints->flags = PMinSize | PMaxSize; hints->flags = PMinSize | PMaxSize;
@@ -55,16 +81,14 @@ extern int plr_display_x11_main(int argc, char *argv[])
GC gc = XCreateGC(display, window, 0, NULL); GC gc = XCreateGC(display, window, 0, NULL);
Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, & wm_delete_window, 1); XSetWMProtocols(display, window, &wm_delete_window, 1);
// TODO: can't be not true // TODO: can't be not true
int bytes_per_pixel = 4; int bytes_per_pixel = 4;
char *pixel_buffer = (char *)malloc(WIDTH * HEIGHT * bytes_per_pixel); char *pixel_buffer = (char *)malloc(WIDTH * HEIGHT * bytes_per_pixel);
XImage *ximage = XCreateImage( XImage *ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
display, visual, depth, ZPixmap, 0, pixel_buffer, WIDTH, HEIGHT, 32, 0);
pixel_buffer, WIDTH, HEIGHT, 32, 0
);
if (!ximage) { if (!ximage) {
fprintf(stderr, "Failed to create XImage\n"); fprintf(stderr, "Failed to create XImage\n");
@@ -79,13 +103,14 @@ extern int plr_display_x11_main(int argc, char *argv[])
while (!quited) { while (!quited) {
XNextEvent(display, &event); XNextEvent(display, &event);
switch(event.type) { switch (event.type) {
case ClientMessage: case ClientMessage:
if(event.xclient.data.l[0] == wm_delete_window) { if (event.xclient.data.l[0] == wm_delete_window) {
XDestroyWindow(display, window); XDestroyWindow(display, window);
quited = true; quited = true;
} }
break; break;
case Expose: case Expose:
frame += 2; frame += 2;
// Fill buffer with a simple color gradient // Fill buffer with a simple color gradient
@@ -101,14 +126,13 @@ extern int plr_display_x11_main(int argc, char *argv[])
} }
} }
// Draw the complete image onto the window when exposed // Draw the complete image onto the window when exposed
XPutImage( XPutImage(display,
display,
window, // Target drawable window, // Target drawable
gc, // Graphics Context gc, // Graphics Context
ximage, // Source XImage ximage, // Source XImage
0, 0, // Source coordinates (x, y) 0, 0, // Source coordinates (x, y)
0, 0, // Destination coordinates (x, y) 0, 0, // Destination coordinates (x, y)
WIDTH, HEIGHT// Dimensions to copy WIDTH, HEIGHT // Dimensions to copy
); );
break; break;
} }
+13 -5
View File
@@ -1,10 +1,18 @@
CC=clang CC=clang
CFLAGS=-lX11 CFLAGS=-lX11 -Wall -std=c99 -g3 -O3 -fno-inline -msse2
DEPS = DEPS = Display/display.h ../Common/lzw.h ../Common/mat.h ../Common/vec.h ../Common/fixed.h ../Common/def.h ../Common/bits.h
OBJ = main.o Display/x11.o OBJ = ../Common/lzw.o ../Common/fixed.o
LINUX = main.o Display/x11.o
DOS = maindos.c Display/dos.c
%.o: %.c $(DEPS) %.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS) $(CC) -c -o $@ $< $(CFLAGS)
player: $(OBJ) linux: $(LINUX) $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(CC) -o player $^ $(CFLAGS)
dos: $(DOS) $(DEPS)
wcl386 -bt=dos4g -ox -l=dos4g $(DOS)
clean:
rm -r *.o ../*.o
View File
+26
View File
@@ -0,0 +1,26 @@
/*
Inter-process communicating socket, useful for local testing and admin
playing.
*/
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#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;
}
+36 -10
View File
@@ -1,13 +1,39 @@
Pustina -- Player Pustina -- Player
BYOND-inspired optimized web-driven content player. Locked step multiplayer oriented engine with great restrictions.
All logic is processed on the server, whereas player only does display changes and sends unreliable inputs.
This allows for no-update and hidden content strategies, allowing for mystic and truly seamlessly progressing world.
We do not use abstractions, as the simplicity of the required set of features should allow for platform-specific implementations. We do not use abstractions, as the simplicity of the required set of features should allow for platform-specific implementations.
Restrictions:
-- Fixed sized frambuffer, default is 640x480, but platforms can implement their own. Scaling by 2 is possible. ---- Restrictions ----
-- GIF image format for all drawing purposes. Delta Frame Optimiztion is encouraged. * Fixed sized paletted frambuffer, defaulted to 640x480. Scaling by 2 is possible.
-- Images are 32x32 or multiples of it, aligning to the grid. * Deterministic logic via Q15.16 fixed point numbers.
-- 20 FPS display rate. * .gam image format, derived from .gif. RLE and Delta Frame Optimiztion is added, palette is predefined.
-- Fonts are in ASCII bitmaps. * Images are 16x16 or multiples of it, aligning to the grid.
-- Audio samples are in vorbis format. Music pieces are written in our own simple tracker format, using the same vorbis samples. * Tile view is capped at 29x29, the rest is allocated to interface.
* 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).
* 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.
---- .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.
Generic LZW compression is applied as the last step.
---- .dab format ---
Compressed streamed content database format.
It allows for seeked access of required portions only via the content table.
Content table stores pathing, content and compression metadata as well as sized offset inside the file.
+5
View File
@@ -0,0 +1,5 @@
#include "Display/display.h"
extern int main(int argc, char *argv[]) {
return plr_display_dos_main(argc, argv);
}