summaryrefslogtreecommitdiff
path: root/src/gfx
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfx')
-rw-r--r--src/gfx/CMakeLists.txt25
-rw-r--r--src/gfx/include/gfx/gfx.h48
-rw-r--r--src/gfx/include/gfx/sprite.h32
-rw-r--r--src/gfx/ps1/CMakeLists.txt40
-rw-r--r--src/gfx/ps1/include/gfx/port.h126
-rw-r--r--src/gfx/ps1/private_include/gfx/private.h19
-rw-r--r--src/gfx/ps1/src/4line.c15
-rw-r--r--src/gfx/ps1/src/CMakeLists.txt32
-rw-r--r--src/gfx/ps1/src/add_to_list.c39
-rw-r--r--src/gfx/ps1/src/deinit.c24
-rw-r--r--src/gfx/ps1/src/draw.c70
-rw-r--r--src/gfx/ps1/src/env.c71
-rw-r--r--src/gfx/ps1/src/heap.c73
-rw-r--r--src/gfx/ps1/src/init.c153
-rw-r--r--src/gfx/ps1/src/quad.c24
-rw-r--r--src/gfx/ps1/src/ready.c27
-rw-r--r--src/gfx/ps1/src/rect.c25
-rw-r--r--src/gfx/ps1/src/sort.c73
-rw-r--r--src/gfx/ps1/src/sprite/CMakeLists.txt22
-rw-r--r--src/gfx/ps1/src/sprite/clone.c26
-rw-r--r--src/gfx/ps1/src/sprite/free.c23
-rw-r--r--src/gfx/ps1/src/sprite/load.c191
-rw-r--r--src/gfx/ps1/src/sprite/sort.c26
-rw-r--r--src/gfx/ps1/src/swapenvs.c58
-rw-r--r--src/gfx/ps1/src/vblank.c21
-rw-r--r--src/gfx/sdl-1.2/CMakeLists.txt19
-rw-r--r--src/gfx/sdl-1.2/include/gfx/port.h58
-rw-r--r--src/gfx/sdl-1.2/private_include/gfx/private.h26
-rw-r--r--src/gfx/sdl-1.2/src/CMakeLists.txt23
-rw-r--r--src/gfx/sdl-1.2/src/env.c204
-rw-r--r--src/gfx/sdl-1.2/src/line.c16
-rw-r--r--src/gfx/sdl-1.2/src/quad.c44
-rw-r--r--src/gfx/sdl-1.2/src/rect.c35
-rw-r--r--src/gfx/sdl-1.2/src/sprite.c143
-rw-r--r--src/gfx/src/CMakeLists.txt19
-rw-r--r--src/gfx/src/dim.c21
36 files changed, 1891 insertions, 0 deletions
diff --git a/src/gfx/CMakeLists.txt b/src/gfx/CMakeLists.txt
new file mode 100644
index 0000000..acb07d2
--- /dev/null
+++ b/src/gfx/CMakeLists.txt
@@ -0,0 +1,25 @@
+# wanix, a Unix-like operating system for WebAssembly applications.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+add_library(gfx)
+add_subdirectory(src)
+target_include_directories(gfx PUBLIC include)
+
+if(PS1_BUILD)
+ add_subdirectory(ps1)
+elseif(SDL1_2_BUILD)
+ add_subdirectory(sdl-1.2)
+endif()
diff --git a/src/gfx/include/gfx/gfx.h b/src/gfx/include/gfx/gfx.h
new file mode 100644
index 0000000..95c602a
--- /dev/null
+++ b/src/gfx/include/gfx/gfx.h
@@ -0,0 +1,48 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GFX_H
+#define GFX_H
+
+#include <gfx/port.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+int gfx_init(void);
+int gfx_draw(void);
+bool gfx_ready(void);
+int gfx_toggle_fullscreen(void);
+int gfx_set_fullscreen(short w, short h);
+bool gfx_fullscreen_available(void);
+bool gfx_fullscreen(void);
+int gfx_display_size(short *w, short *h);
+
+void gfx_rect_init(struct gfx_rect *r);
+void semitrans_rect_init(struct gfx_rect *r);
+void stp_4line_init(struct stp_4line *l);
+void quad_sort(struct quad *q);
+void gfx_rect_sort(struct gfx_rect *r);
+void stp_4line_sort(struct stp_4line *l);
+int sprite_from_fp(struct gfx_sprite *s, FILE *f);
+int quad_from_sprite(const struct gfx_sprite *s, struct quad *q);
+bool gfx_inside_drawenv(short x, short y, short w, short h);
+void gfx_deinit(void);
+
+extern int screen_w, screen_h;
+
+#endif /* GFX_H */
diff --git a/src/gfx/include/gfx/sprite.h b/src/gfx/include/gfx/sprite.h
new file mode 100644
index 0000000..9746524
--- /dev/null
+++ b/src/gfx/include/gfx/sprite.h
@@ -0,0 +1,32 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GFX_SPRITE_H
+#define GFX_SPRITE_H
+
+#include <gfx/port.h>
+#include <stddef.h>
+
+typedef int (*gfx_read)(void *buf, size_t n, void *args);
+
+int gfx_sprite_load(struct gfx_sprite *s, gfx_read r, void *args);
+int gfx_sprite_sort(struct gfx_sprite *s);
+int gfx_sprite_clone(const struct gfx_sprite *src, struct gfx_sprite *dst);
+void gfx_sprite_free(struct gfx_sprite *s);
+
+#endif
diff --git a/src/gfx/ps1/CMakeLists.txt b/src/gfx/ps1/CMakeLists.txt
new file mode 100644
index 0000000..1de20a7
--- /dev/null
+++ b/src/gfx/ps1/CMakeLists.txt
@@ -0,0 +1,40 @@
+# wanix, a Unix-like operating system for WebAssembly applications.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+add_subdirectory(src)
+target_include_directories(gfx PUBLIC include PRIVATE private_include)
+target_link_libraries(gfx PRIVATE
+ drv_ps1_bios
+ drv_ps1_dma
+ drv_ps1_interrupt
+ drv_ps1_gpu
+)
+
+set(modes VMODE_PAL VMODE_NTSC)
+
+if(VIDEO_MODE)
+ if(NOT "${VIDEO_MODE}" IN_LIST modes)
+ message(FATAL_ERROR "Invalid video mode ${VIDEO_MODE}. Available options:\n"
+ "${modes}\n"
+ "Run CMake again using one of the available video modes e.g.: cmake .. -DVIDEO_MODE=VMODE_PAL")
+ endif()
+
+ target_compile_definitions(gfx PRIVATE VIDEO_MODE=${VIDEO_MODE})
+else()
+ message(FATAL_ERROR "Please define video mode. Available options:\n"
+ "${modes}\n"
+ "Run CMake again using one of the available video modes e.g.: cmake .. -DVIDEO_MODE=VMODE_PAL")
+endif()
diff --git a/src/gfx/ps1/include/gfx/port.h b/src/gfx/ps1/include/gfx/port.h
new file mode 100644
index 0000000..2e609d1
--- /dev/null
+++ b/src/gfx/ps1/include/gfx/port.h
@@ -0,0 +1,126 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GFX_PS1_H
+#define GFX_PS1_H
+
+#include <stdint.h>
+
+union gfx_common
+{
+ struct
+ {
+ uint32_t tpagex :4, tpagey :1, stp :2, bpp :2, dither :1,
+ draw_to_disp :1, disable :1, xflip :1, yflip :1, :10;
+ uint8_t cmd;
+ } f;
+
+ uint32_t mask;
+};
+
+union gfx_sznext
+{
+ struct
+ {
+ uint32_t next :24, sz :8;
+ } f;
+
+ uint32_t cmd_next;
+};
+
+struct gfx_sprite
+{
+ union gfx_sznext sznext;
+ union gfx_common common;
+ uint8_t r, g, b;
+ uint8_t cmd;
+ int16_t x, y;
+ uint8_t u, v;
+ uint16_t clutid;
+ uint16_t w, h;
+};
+
+struct quad
+{
+ union gfx_sznext sznext;
+ uint8_t r, g, b;
+ uint8_t cmd;
+ int16_t x0, y0;
+ uint8_t u0, v0;
+ uint16_t clutid;
+ int16_t x1, y1;
+ uint8_t u1, v1;
+
+ union
+ {
+ struct
+ {
+ /* 0-8 Same as GP0(E1h).Bit0-8. */
+ uint16_t lb :9;
+ uint16_t :2;
+ /* 11 Same as GP0(E1h).Bit11. */
+ uint16_t hb :1;
+ uint16_t :4;
+ } bit;
+
+ uint16_t mask;
+ } tpage;
+
+ int16_t x2, y2;
+ uint8_t u2, v2;
+ uint16_t :16;
+ int16_t x3, y3;
+ uint8_t u3, v3;
+ uint16_t :16;
+};
+
+struct gfx_rect
+{
+ union gfx_sznext sznext;
+ union gfx_common common;
+ uint8_t r, g, b;
+ uint8_t cmd;
+ int16_t x, y;
+ uint16_t w, h;
+};
+
+struct stp_4line
+{
+ union gfx_sznext sznext;
+ union gfx_common common;
+ uint8_t r, g, b;
+ uint8_t cmd;
+ int16_t x, y;
+
+ struct stp_4line_vtx
+ {
+ uint32_t r :8, g :8, b :8, :8;
+ int16_t x, y;
+ } vertices[4];
+
+ uint32_t end;
+};
+
+void gfx_add_to_list(union gfx_sznext *p);
+void *gfx_heap_top(void);
+struct gfx_sprite *gfx_sprite_get(void);
+struct quad *quad_get(void);
+struct gfx_rect *rect_get(void);
+struct stp_4line *stp_4line_get(void);
+
+#endif
diff --git a/src/gfx/ps1/private_include/gfx/private.h b/src/gfx/ps1/private_include/gfx/private.h
new file mode 100644
index 0000000..72d8f39
--- /dev/null
+++ b/src/gfx/ps1/private_include/gfx/private.h
@@ -0,0 +1,19 @@
+#ifndef GFX_PS1_PRIVATE_H
+#define GFX_PS1_PRIVATE_H
+
+#include <gfx/gfx.h>
+
+enum
+{
+ SCREEN_W = 640,
+ SCREEN_H = 480
+};
+
+void gfx_swapheap(void);
+void gfx_initenvs(void);
+void gfx_swapbuffers(void);
+void gfx_swapenvs(void);
+
+extern int gfx_vblank_ev;
+
+#endif
diff --git a/src/gfx/ps1/src/4line.c b/src/gfx/ps1/src/4line.c
new file mode 100644
index 0000000..766eb2d
--- /dev/null
+++ b/src/gfx/ps1/src/4line.c
@@ -0,0 +1,15 @@
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <psxgpu.h>
+#include <stdint.h>
+#include <string.h>
+
+void stp_4line_init(struct stp_4line *const l)
+{
+ memset(l, 0, sizeof *l);
+ l->sznext.f.sz = (sizeof *l - sizeof l->sznext) / sizeof (uint32_t);
+ l->common.f.cmd = DRAW_MODE;
+ l->cmd = 0x5A;
+ enum {TERMINATION_CODE = 0x55555555};
+ l->end = TERMINATION_CODE;
+}
diff --git a/src/gfx/ps1/src/CMakeLists.txt b/src/gfx/ps1/src/CMakeLists.txt
new file mode 100644
index 0000000..418bd3b
--- /dev/null
+++ b/src/gfx/ps1/src/CMakeLists.txt
@@ -0,0 +1,32 @@
+# wanix, a Unix-like operating system for WebAssembly applications.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+target_sources(gfx PRIVATE
+ # 4line.c
+ # env.c
+ draw.c
+ heap.c
+ init.c
+ ready.c
+ swapenvs.c
+ # quad.c
+ # rect.c
+ # sort.c
+ # sprite.c
+ vblank.c
+)
+
+add_subdirectory(sprite)
diff --git a/src/gfx/ps1/src/add_to_list.c b/src/gfx/ps1/src/add_to_list.c
new file mode 100644
index 0000000..dff4910
--- /dev/null
+++ b/src/gfx/ps1/src/add_to_list.c
@@ -0,0 +1,39 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <stddef.h>
+#include <stdint.h>
+
+static union gfx_sznext *first, *last;
+
+void gfx_add_to_list(union gfx_sznext *const p)
+{
+ if (!first)
+ first = p;
+ else if (last)
+ last->f.next = (uint32_t)p;
+
+ last = p;
+}
+
+void *gfx_heap_top(void)
+{
+ return first;
+}
diff --git a/src/gfx/ps1/src/deinit.c b/src/gfx/ps1/src/deinit.c
new file mode 100644
index 0000000..ccf6c2d
--- /dev/null
+++ b/src/gfx/ps1/src/deinit.c
@@ -0,0 +1,24 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+
+void gfx_deinit(void)
+{
+}
diff --git a/src/gfx/ps1/src/draw.c b/src/gfx/ps1/src/draw.c
new file mode 100644
index 0000000..40abdf7
--- /dev/null
+++ b/src/gfx/ps1/src/draw.c
@@ -0,0 +1,70 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <drv/ps1/dma.h>
+#include <drv/ps1/gpu.h>
+
+static union gfx_sznext *first, *last;
+
+void gfx_add_to_list(union gfx_sznext *const p)
+{
+ if (!first)
+ first = p;
+ else if (last)
+ last->f.next = (uint32_t)p;
+
+ last = p;
+}
+
+int gfx_draw(void)
+{
+ static union gfx_sznext term = {.cmd_next = 0xffffff};
+
+ gfx_add_to_list(&term);
+
+ if (SCREEN_W != 640)
+ gfx_swapenvs();
+
+ gfx_swapheap();
+
+ GP1->mask = (const union drv_ps1_gpu_gp1)
+ {
+ .dma =
+ {
+ .cmd = GP1_DMA_DIR,
+ .dir = GP1_DMA_DIR_CPU_TO_GP0
+ }
+ }.mask;
+
+ D2_MADR->mask = (uint32_t)first;
+ D2_BCR->syncmode_2.reserved = 0;
+ D2_CHCR->mask = (const union chcr)
+ {
+ .bits =
+ {
+ .sync_mode = CHCR_SYNC_MODE_LINKED_LIST,
+ .dir = CHCR_DIR_FROM_RAM,
+ .start_busy = 1
+ }
+ }.mask;
+
+ first = NULL;
+ return 0;
+}
diff --git a/src/gfx/ps1/src/env.c b/src/gfx/ps1/src/env.c
new file mode 100644
index 0000000..100f366
--- /dev/null
+++ b/src/gfx/ps1/src/env.c
@@ -0,0 +1,71 @@
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <psxgpu.h>
+#include <errno.h>
+#include <stdbool.h>
+
+static GsDispEnv dispenv;
+enum {ENV_Y = SCREEN_H + 16};
+
+static GsDrawEnv drawenv =
+{
+ .y = ENV_Y,
+ .w = SCREEN_W,
+ .h = SCREEN_H
+};
+
+int screen_w = SCREEN_W, screen_h = SCREEN_H;
+
+void gfx_swapbuffers(void)
+{
+ const short y = drawenv.y;
+
+ drawenv.y = dispenv.y;
+ dispenv.y = y;
+ GsSetDrawEnv(&drawenv);
+ GsSetDispEnv(&dispenv);
+}
+
+void gfx_initenvs(void)
+{
+ GsSetDrawEnv(&drawenv);
+ GsSetDispEnv(&dispenv);
+}
+
+int gfx_toggle_fullscreen(void)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+int gfx_set_fullscreen(const short w, const short h)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+bool gfx_fullscreen_available(void)
+{
+ return false;
+}
+
+bool gfx_fullscreen(void)
+{
+ return true;
+}
+
+int gfx_display_size(short *const w, short *const h)
+{
+ *w = SCREEN_W;
+ *h = SCREEN_H;
+ return 0;
+}
+
+bool gfx_inside_drawenv(const short x, const short y, const short w,
+ const short h)
+{
+ return (x + w >= 0)
+ && x < drawenv.w
+ && (y + h >= 0)
+ && y < drawenv.h;
+}
diff --git a/src/gfx/ps1/src/heap.c b/src/gfx/ps1/src/heap.c
new file mode 100644
index 0000000..feb95c9
--- /dev/null
+++ b/src/gfx/ps1/src/heap.c
@@ -0,0 +1,73 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <stddef.h>
+
+static unsigned int sel;
+static size_t heap_i;
+
+void gfx_swapheap(void)
+{
+ sel ^= 1;
+ heap_i = 0;
+}
+
+static void *get_element(const size_t sz)
+{
+ enum {HEAP_SZ = 49152, N_HEAPS = 2};
+ static char heaps[N_HEAPS][HEAP_SZ];
+ const size_t new_sz = heap_i + sz;
+ void *ret = NULL;
+
+ if (new_sz < sizeof *heaps / sizeof **heaps)
+ {
+ ret = &heaps[sel][heap_i];
+ heap_i += sz;
+ }
+ else
+ {
+ static volatile int a;
+
+ a++;
+ return NULL;
+ }
+
+ return ret;
+}
+
+struct gfx_sprite *gfx_sprite_get(void)
+{
+ return get_element(sizeof (struct gfx_sprite));
+}
+
+struct quad *quad_get(void)
+{
+ return get_element(sizeof (struct quad));
+}
+
+struct gfx_rect *rect_get(void)
+{
+ return get_element(sizeof (struct gfx_rect));
+}
+
+struct stp_4line *stp_4line_get(void)
+{
+ return get_element(sizeof (struct stp_4line));
+}
diff --git a/src/gfx/ps1/src/init.c b/src/gfx/ps1/src/init.c
new file mode 100644
index 0000000..3a94f32
--- /dev/null
+++ b/src/gfx/ps1/src/init.c
@@ -0,0 +1,153 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <drv/ps1/bios.h>
+#include <drv/ps1/dma.h>
+#include <drv/ps1/gpu.h>
+#include <drv/ps1/interrupt.h>
+#include <stddef.h>
+
+struct res
+{
+ int hres, hres2, vres, interlace;
+};
+
+static void getres(const short w, struct res *const r)
+{
+ *r = (const struct res){0};
+
+ switch (w)
+ {
+ case 320:
+ r->hres = 1;
+ break;
+ case 368:
+ r->hres2 = 1;
+ break;
+ case 512:
+ r->hres = 2;
+ break;
+ case 640:
+ r->hres = 3;
+ r->vres = 1;
+ r->interlace = 1;
+ break;
+ }
+}
+
+int gfx_init(void)
+{
+ const union dpcr dpcr = {.bits.gpu_en = 1};
+ const int event = OpenEvent(CLASS_VBLANK, SPEC_INTERRUPTED, MODE_READY,
+ NULL);
+
+ if (event == -1)
+ return -1;
+
+ DPCR->mask |= dpcr.mask;
+ GP1->mask = (const union drv_ps1_gpu_gp1)
+ {
+ .dma =
+ {
+ .cmd = GP1_DMA_DIR,
+ .dir = GP1_DMA_DIR_CPU_TO_GP0
+ }
+ }.mask;
+
+ GP1->mask = (const union drv_ps1_gpu_gp1)
+ {
+ .display =
+ {
+ .cmd = GP1_DISPLAY_ENABLE,
+ }
+ }.mask;
+
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {.draw_mode.cmd = GP0_DRAW_MODE}.mask;
+
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {.tex_window.cmd = GP0_TEX_WINDOW}.mask;
+
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {.draw_area_tl.cmd = GP0_DRAW_AREA_TOP_LEFT}.mask;
+
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {
+ .draw_area_br =
+ {
+ .cmd = GP0_DRAW_AREA_BOTTOM_RIGHT,
+ .x = SCREEN_W - 1,
+ .y = SCREEN_H - 1,
+ }
+ }.mask;
+
+ GP1->mask = (const union drv_ps1_gpu_gp1)
+ {
+ .h_display_range =
+ {
+ .cmd = GP1_H_DISPLAY_RANGE,
+ .x1 = 0x260,
+ .x2 = 0x260 + 320 * 8
+ }
+ }.mask;
+
+#if 0
+ GP1->mask = (const union drv_ps1_gpu_gp1)
+ {
+ .v_display_range =
+ {
+ .cmd = GP1_V_DISPLAY_RANGE,
+#if VIDEO_MODE == VMODE_PAL
+ .y1 = 0xa3,
+ .y2 = 0xa3 + (SCREEN_H / 2)
+#elif VIDEO_MODE == VMODE_NTSC
+ .y1 = 0x88 - (224 / 2),
+ .y2 = 0x88 + (224 / 2)
+#endif
+ }
+ }.mask;
+#else
+ GP1->mask = 0x06C4E24E;
+ GP1->mask = 0x07040010;
+#endif
+
+ struct res r;
+
+ getres(SCREEN_W, &r);
+
+ GP1->mask = (const union drv_ps1_gpu_gp1)
+ {
+ .display_mode =
+ {
+ .cmd = GP1_DISPLAY_MODE,
+ .hres = r.hres,
+ .hres2 = r.hres2,
+ .vres = r.vres,
+ .vinterlace = r.interlace,
+#if VIDEO_MODE == VMODE_PAL
+ .vmode = 1
+#endif
+ }
+ }.mask;
+
+ I_MASK->bits.vblank = 1;
+ EnableEvent(gfx_vblank_ev = event);
+ return 0;
+}
diff --git a/src/gfx/ps1/src/quad.c b/src/gfx/ps1/src/quad.c
new file mode 100644
index 0000000..bfabee2
--- /dev/null
+++ b/src/gfx/ps1/src/quad.c
@@ -0,0 +1,24 @@
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <gfx/port.h>
+#include <stdint.h>
+#include <string.h>
+
+static void quad_init(struct quad *const q)
+{
+ memset(q, 0, sizeof *q);
+ q->sznext.f.sz = (sizeof *q - sizeof q->sznext) / sizeof (uint32_t);
+ q->cmd = 0x2d;
+}
+
+int quad_from_sprite(const struct gfx_sprite *const s, struct quad *const q)
+{
+ quad_init(q);
+ q->tpage.mask = s->common.mask;
+ q->clutid = s->clutid;
+ q->u0 = q->u2 = s->u;
+ q->v0 = q->v1 = s->v;
+ q->u1 = q->u3 = s->u + s->w - 1;
+ q->v2 = q->v3 = s->v + s->h - 1;
+ return 0;
+}
diff --git a/src/gfx/ps1/src/ready.c b/src/gfx/ps1/src/ready.c
new file mode 100644
index 0000000..0e17cdb
--- /dev/null
+++ b/src/gfx/ps1/src/ready.c
@@ -0,0 +1,27 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <drv/ps1/bios.h>
+#include <stdbool.h>
+
+bool gfx_ready(void)
+{
+ return TestEvent(gfx_vblank_ev);
+}
diff --git a/src/gfx/ps1/src/rect.c b/src/gfx/ps1/src/rect.c
new file mode 100644
index 0000000..57b1b2a
--- /dev/null
+++ b/src/gfx/ps1/src/rect.c
@@ -0,0 +1,25 @@
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <psxgpu.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+static void common_init(struct gfx_rect *const r)
+{
+ memset(r, 0, sizeof *r);
+ r->sznext.f.sz = (sizeof *r - sizeof r->sznext) / sizeof (uint32_t);
+ r->common.f.cmd = DRAW_MODE;
+}
+
+void gfx_rect_init(struct gfx_rect *const r)
+{
+ common_init(r);
+ r->cmd = 0x60;
+}
+
+void semitrans_rect_init(struct gfx_rect *const r)
+{
+ common_init(r);
+ r->cmd = 0x62;
+}
diff --git a/src/gfx/ps1/src/sort.c b/src/gfx/ps1/src/sort.c
new file mode 100644
index 0000000..5c1c278
--- /dev/null
+++ b/src/gfx/ps1/src/sort.c
@@ -0,0 +1,73 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <drv/ps1/bios.h>
+#include <stddef.h>
+#include <stdint.h>
+
+static union gfx_sznext *first, *last;
+
+static void add_to_list(union gfx_sznext *const p)
+{
+ if (!first)
+ first = p;
+ else if (last)
+ last->f.next = (uint32_t)p;
+
+ last = p;
+}
+
+void sprite_sort(struct gfx_sprite *const s)
+{
+ add_to_list(&s->sznext);
+}
+
+void quad_sort(struct quad *const q)
+{
+ add_to_list(&q->sznext);
+}
+
+void gfx_rect_sort(struct gfx_rect *const r)
+{
+ add_to_list(&r->sznext);
+}
+
+void stp_4line_sort(struct stp_4line *const l)
+{
+ add_to_list(&l->sznext);
+}
+
+int gfx_draw(void)
+{
+ static union gfx_sznext term = {.cmd_next = 0xffffff};
+
+ add_to_list(&term);
+
+ void gpu_ctrl(unsigned int command, unsigned int param);
+
+ gfx_swapbuffers();
+ gfx_swapheap();
+ gpu_ctrl(4, 2);
+ D2_MADR = (uint32_t)first;
+ D2_BCR = 0;
+ D2_CHCR = (1 << 0xa) | 1 | (1 << 0x18);
+ first = NULL;
+ return 0;
+}
diff --git a/src/gfx/ps1/src/sprite/CMakeLists.txt b/src/gfx/ps1/src/sprite/CMakeLists.txt
new file mode 100644
index 0000000..b42d699
--- /dev/null
+++ b/src/gfx/ps1/src/sprite/CMakeLists.txt
@@ -0,0 +1,22 @@
+# wanix, a Unix-like operating system for WebAssembly applications.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+target_sources(gfx PRIVATE
+ clone.c
+ free.c
+ load.c
+ sort.c
+)
diff --git a/src/gfx/ps1/src/sprite/clone.c b/src/gfx/ps1/src/sprite/clone.c
new file mode 100644
index 0000000..30d0d32
--- /dev/null
+++ b/src/gfx/ps1/src/sprite/clone.c
@@ -0,0 +1,26 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+
+int gfx_sprite_clone(const struct gfx_sprite *const src,
+ struct gfx_sprite *const dst)
+{
+ *dst = *src;
+ return 0;
+}
diff --git a/src/gfx/ps1/src/sprite/free.c b/src/gfx/ps1/src/sprite/free.c
new file mode 100644
index 0000000..3b3d984
--- /dev/null
+++ b/src/gfx/ps1/src/sprite/free.c
@@ -0,0 +1,23 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+
+void sprite_free(struct gfx_sprite *const src)
+{
+}
diff --git a/src/gfx/ps1/src/sprite/load.c b/src/gfx/ps1/src/sprite/load.c
new file mode 100644
index 0000000..7664017
--- /dev/null
+++ b/src/gfx/ps1/src/sprite/load.c
@@ -0,0 +1,191 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/sprite.h>
+#include <gfx/private.h>
+#include <drv/ps1/gpu.h>
+#include <stdint.h>
+
+static void init(struct gfx_sprite *const s)
+{
+ *s = (const struct gfx_sprite){0};
+ s->sznext.f.sz = (sizeof *s - sizeof s->sznext) / sizeof (uint32_t);
+ s->common.f.cmd = GP0_DRAW_MODE;
+ s->cmd = GP0_TEXTRECT_VARSZ_OPAQ_RAW;
+}
+
+struct tim_pos
+{
+ uint16_t x, y, w, h;
+};
+
+static void transfer_init(const struct tim_pos *const p)
+{
+ while (!GPUSTAT->bits.ready_cmd)
+ ;
+
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {.copy_rect.cmd = GP0_COPY_RECT_CPU_VRAM}.mask;
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {.coord = {.x = p->x, .y = p->y}}.mask;
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {.size = {.w = p->w, .h = p->h}}.mask;
+}
+
+static int transfer(const size_t sz, const gfx_read r, void *const args)
+{
+ const size_t rem = sz % sizeof (uint32_t);
+
+ if (sz >= sizeof (uint32_t))
+ {
+ for (size_t i = 0; i < sz / sizeof (uint32_t); i++)
+ {
+ uint32_t pix;
+
+ if (r(&pix, sizeof pix, args))
+ return -1;
+
+ GP0->mask = pix;
+ }
+ }
+
+ if (rem)
+ {
+ uint32_t pix = 0;
+
+ if (r(&pix, rem, args))
+ return -1;
+
+ GP0->mask = pix;
+ }
+
+ return 0;
+}
+
+struct header
+{
+ uint32_t sz;
+ struct tim_pos pos;
+};
+
+static short get_clutid(const short x, const short y)
+{
+ return (x & 0x3ff) >> 4 | (y & 0x1ff) << 6;
+}
+
+static int upload_clut(struct gfx_sprite *const s, const gfx_read r,
+ void *const args)
+{
+ struct header clut;
+
+ if (r(&clut, sizeof clut, args))
+ return -1;
+
+ transfer_init(&clut.pos);
+
+ const size_t sz = clut.sz - sizeof clut;
+
+ if (transfer(sz, r, args))
+ return -1;
+
+ s->clutid = get_clutid(clut.pos.x, clut.pos.y);
+ return 0;
+}
+
+enum bpp
+{
+ BPP_4 = 0,
+ BPP_8 = 1,
+ BPP_16 = 2,
+ BPP_24 = 4
+};
+
+static int upload_img(struct gfx_sprite *const s, const enum bpp bpp,
+ const gfx_read r, void *const args)
+{
+ struct header img;
+
+ if (r(&img, sizeof img, args))
+ return -1;
+
+ transfer_init(&img.pos);
+
+ const size_t sz = img.sz - sizeof img;
+
+ if (transfer(sz, r, args))
+ return -1;
+
+ enum
+ {
+ VRAM_X = 1024,
+ VRAM_Y = 512,
+ TPAGE_WIDTH = 64
+ };
+
+ s->common.f.tpagex = img.pos.x / TPAGE_WIDTH;
+ s->common.f.tpagey = img.pos.y / (VRAM_Y / 2);
+ s->u = img.pos.x % TPAGE_WIDTH;
+ s->v = img.pos.y % (VRAM_Y / 2);
+
+ switch (bpp)
+ {
+ case BPP_4:
+ s->w = img.pos.w * 4;
+ s->u <<= 2;
+ break;
+
+ case BPP_8:
+ s->w = img.pos.w * 2;
+ s->u <<= 1;
+ break;
+
+ case BPP_16:
+ s->w = img.pos.w;
+ break;
+
+ case BPP_24:
+ s->w = img.pos.w + (img.pos.w / 2);
+ break;
+ }
+
+ s->h = img.pos.h;
+ return 0;
+}
+
+int gfx_sprite_load(struct gfx_sprite *s, const gfx_read r, void *const args)
+{
+ init(s);
+
+ struct
+ {
+ uint32_t version, bpp :3, has_clut :1, :28;
+ } h;
+
+ enum {VERSION_ID = 0x10};
+
+ if (r(&h, sizeof h, args)
+ || h.version != VERSION_ID
+ || h.bpp == BPP_24
+ || (h.has_clut && upload_clut(s, r, args))
+ || upload_img(s, h.bpp, r, args))
+ return -1;
+
+ s->common.f.bpp = h.bpp ? __builtin_ctz(h.bpp) + 1 : 0;
+ return 0;
+}
diff --git a/src/gfx/ps1/src/sprite/sort.c b/src/gfx/ps1/src/sprite/sort.c
new file mode 100644
index 0000000..76fc214
--- /dev/null
+++ b/src/gfx/ps1/src/sprite/sort.c
@@ -0,0 +1,26 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/port.h>
+
+int gfx_sprite_sort(struct gfx_sprite *const s)
+{
+ gfx_add_to_list(&s->sznext);
+ return 0;
+}
diff --git a/src/gfx/ps1/src/swapenvs.c b/src/gfx/ps1/src/swapenvs.c
new file mode 100644
index 0000000..9e120ad
--- /dev/null
+++ b/src/gfx/ps1/src/swapenvs.c
@@ -0,0 +1,58 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <drv/ps1/gpu.h>
+
+void gfx_swapenvs(void)
+{
+ static short dispenv, drawenv = 256;
+ const short y = dispenv;
+
+ dispenv = drawenv;
+ drawenv = y;
+
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {
+ .draw_area_tl =
+ {
+ .cmd = GP0_DRAW_AREA_TOP_LEFT,
+ .y = drawenv
+ }
+ }.mask;
+
+ GP0->mask = (const union drv_ps1_gpu_gp0)
+ {
+ .draw_area_br =
+ {
+ .cmd = GP0_DRAW_AREA_BOTTOM_RIGHT,
+ .x = SCREEN_W - 1,
+ .y = drawenv + SCREEN_H - 1,
+ }
+ }.mask;
+
+ GP1->mask = (const union drv_ps1_gpu_gp1)
+ {
+ .disparea =
+ {
+ .cmd = GP1_START_DISPLAY_AREA,
+ .y = dispenv
+ }
+ }.mask;
+}
diff --git a/src/gfx/ps1/src/vblank.c b/src/gfx/ps1/src/vblank.c
new file mode 100644
index 0000000..cc445c7
--- /dev/null
+++ b/src/gfx/ps1/src/vblank.c
@@ -0,0 +1,21 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/private.h>
+
+int gfx_vblank_ev;
diff --git a/src/gfx/sdl-1.2/CMakeLists.txt b/src/gfx/sdl-1.2/CMakeLists.txt
new file mode 100644
index 0000000..11d2245
--- /dev/null
+++ b/src/gfx/sdl-1.2/CMakeLists.txt
@@ -0,0 +1,19 @@
+# wanix, a Unix-like operating system for WebAssembly applications.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+add_subdirectory(src)
+target_include_directories(gfx PUBLIC include PRIVATE private_include)
+target_link_libraries(gfx PUBLIC SDL::SDL PRIVATE SDL::SDL_gfx)
diff --git a/src/gfx/sdl-1.2/include/gfx/port.h b/src/gfx/sdl-1.2/include/gfx/port.h
new file mode 100644
index 0000000..bf94a38
--- /dev/null
+++ b/src/gfx/sdl-1.2/include/gfx/port.h
@@ -0,0 +1,58 @@
+#ifndef GFX_SDL_H
+#define GFX_SDL_H
+
+#include <SDL.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct gfx_sprite
+{
+ SDL_Surface *s, *s_x;
+ short x, y, w, h;
+ unsigned char u, v;
+ bool transparent;
+};
+
+struct quad
+{
+ unsigned char r, g, b;
+ short x0, x1, x2, x3;
+ short y0, y1, y2, y3;
+ unsigned char u0, u1, u2, u3;
+ unsigned char v0, v1, v2, v3;
+ short w, h;
+ bool transparent;
+ SDL_Surface *s, *s_x;
+};
+
+struct gfx_rect
+{
+ unsigned char r, g, b;
+ short x, y, w, h;
+ bool stp;
+};
+
+struct stp_4line
+{
+ short x, y;
+ unsigned char r, g, b;
+
+ struct stp_4line_vtx
+ {
+ unsigned char r, g, b;
+ short x, y;
+ } vertices[4];
+};
+
+#define common_get_or_ret(t, x, ret) \
+ struct t x##__, *const x = &x##__
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_SDL_H */
diff --git a/src/gfx/sdl-1.2/private_include/gfx/private.h b/src/gfx/sdl-1.2/private_include/gfx/private.h
new file mode 100644
index 0000000..e1af764
--- /dev/null
+++ b/src/gfx/sdl-1.2/private_include/gfx/private.h
@@ -0,0 +1,26 @@
+#ifndef GFX_SDL_12_PRIVATE_H
+#define GFX_SDL_12_PRIVATE_H
+
+#include <gfx/gfx.h>
+#include <SDL.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum
+{
+ SCREEN_W = 320,
+ SCREEN_H = 240
+};
+
+int sprite_screen_resize_ev(struct gfx_sprite *s);
+void gfx_register_sprite(struct gfx_sprite *s);
+SDL_Surface *gfx_screen(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_SDL_12_PRIVATE_H */
diff --git a/src/gfx/sdl-1.2/src/CMakeLists.txt b/src/gfx/sdl-1.2/src/CMakeLists.txt
new file mode 100644
index 0000000..4cef50b
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/CMakeLists.txt
@@ -0,0 +1,23 @@
+# wanix, a Unix-like operating system for WebAssembly applications.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+target_sources(gfx PRIVATE
+ env.c
+ line.c
+ quad.c
+ rect.c
+ sprite.c
+)
diff --git a/src/gfx/sdl-1.2/src/env.c b/src/gfx/sdl-1.2/src/env.c
new file mode 100644
index 0000000..2a51b97
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/env.c
@@ -0,0 +1,204 @@
+#include <gfx/gfx.h>
+#include <gfx/private.h>
+#include <sdl-1.2/gfx/private.h>
+#include <SDL.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int screen_w = SCREEN_W, screen_h = SCREEN_H;
+static bool fullscreen;
+static SDL_Surface *screen;
+static struct gfx_sprite **list;
+static size_t list_len;
+
+void gfx_deinit(void)
+{
+ /* screen should be already freed by SDL_Quit. */
+ free(list);
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+static struct
+{
+ int w, h;
+} display, windowed;
+
+void gfx_register_sprite(struct gfx_sprite *const s)
+{
+ list = realloc(list, (list_len + 1) * sizeof *list);
+
+ if (list)
+ list[list_len++] = s;
+}
+
+static int resize_screen(int w, int h, const bool full_screen)
+{
+ Uint32 flags = SDL_HWSURFACE | SDL_RESIZABLE | SDL_ANYFORMAT | SDL_DOUBLEBUF;
+
+ const SDL_VideoInfo *const info = SDL_GetVideoInfo();
+
+ if (!info)
+ {
+ fprintf(stderr, "SDL_GetVideoInfo: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ static bool init;
+
+ if (!init)
+ {
+ display.w = info->current_w;
+ display.h = info->current_h;
+ init = true;
+ }
+
+ if (fullscreen)
+ {
+ flags |= SDL_FULLSCREEN;
+ w = display.w;
+ h = display.h;
+ }
+ else
+ {
+ windowed.w = w;
+ windowed.h = h;
+ }
+
+ int bpp = info->vfmt->BitsPerPixel;
+
+ if (screen)
+ SDL_FreeSurface(screen);
+
+ const int max_bpp = SDL_VideoModeOK(w, h, 0, flags);
+
+ if (max_bpp < 0)
+ {
+ fprintf(stderr, "SDL_VideoModeOK: %s\n", SDL_GetError());
+ return -1;
+ }
+ else if (bpp > max_bpp)
+ bpp = max_bpp;
+
+ if (!(screen = SDL_SetVideoMode(w, h, 0, flags)))
+ {
+ fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ for (size_t i = 0; i < list_len; i++)
+ if (sprite_screen_resize_ev(list[i]))
+ return -1;
+
+ screen_w = w;
+ screen_h = h;
+ fullscreen = full_screen;
+ return 0;
+}
+
+SDL_Surface *gfx_screen(void)
+{
+ return screen;
+}
+
+int gfx_display_size(short *const w, short *const h)
+{
+ *w = display.w;
+ *h = display.h;
+ return 0;
+}
+
+int gfx_init(void)
+{
+ if (SDL_InitSubSystem(SDL_INIT_VIDEO))
+ {
+ fprintf(stderr, "%s: SDL_InitSubSystem: %s\n",
+ __func__, SDL_GetError());
+ return -1;
+ }
+
+ return resize_screen(screen_w, screen_h, fullscreen);
+}
+
+bool gfx_inside_drawenv(const short x, const short y, const short w,
+ const short h)
+{
+ return (x + w >= 0)
+ && x < screen_w
+ && (y + h >= 0)
+ && y < screen_h;
+}
+
+static int get_resize_events(void)
+{
+ int n;
+ SDL_Event ev;
+
+ while ((n = SDL_PeepEvents(&ev, 1, SDL_GETEVENT,
+ SDL_VIDEORESIZEMASK)) > 0)
+ {
+ if (ev.type == SDL_VIDEORESIZE)
+ {
+ const SDL_ResizeEvent *const res = &ev.resize;
+
+ if (resize_screen(res->w, res->h, false))
+ return -1;
+ }
+ }
+
+ if (n < 0)
+ {
+ fprintf(stderr, "%s: SDL_PeepEvents: %s\n",
+ __func__, SDL_GetError());
+ return -1;
+ }
+
+ return 0;
+}
+
+int gfx_toggle_fullscreen(void)
+{
+ fullscreen ^= true;
+
+ const int w = fullscreen ? display.w : windowed.w;
+ const int h = fullscreen ? display.h : windowed.h;
+
+ return resize_screen(w, h, fullscreen);
+}
+
+int gfx_set_fullscreen(const short w, const short h)
+{
+ return resize_screen(display.w = w, display.h = h, fullscreen = true);
+}
+
+bool gfx_fullscreen_available(void)
+{
+ return true;
+}
+
+bool gfx_fullscreen(void)
+{
+ return fullscreen;
+}
+
+int gfx_draw(void)
+{
+ enum {FPS = 50, REFRESH_MS = 1000 / FPS};
+ static Uint32 prev;
+ const Uint32 cur = SDL_GetTicks();
+
+ if (cur - prev < REFRESH_MS)
+ SDL_Delay(REFRESH_MS - (cur - prev));
+
+ prev = SDL_GetTicks();
+
+ if (SDL_Flip(screen))
+ {
+ fprintf(stderr, "SDL_Flip: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ get_resize_events();
+ return 0;
+}
diff --git a/src/gfx/sdl-1.2/src/line.c b/src/gfx/sdl-1.2/src/line.c
new file mode 100644
index 0000000..b06a414
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/line.c
@@ -0,0 +1,16 @@
+#include <gfx/gfx.h>
+#include <gfx/port.h>
+#include <sdl-1.2/gfx/private.h>
+#include <stddef.h>
+
+void stp_4line_init(struct stp_4line *const l)
+{
+}
+
+void semitrans_stp_4line_init(struct stp_4line *r)
+{
+}
+
+void stp_4line_sort(struct stp_4line *const r)
+{
+}
diff --git a/src/gfx/sdl-1.2/src/quad.c b/src/gfx/sdl-1.2/src/quad.c
new file mode 100644
index 0000000..1617a8b
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/quad.c
@@ -0,0 +1,44 @@
+#include <gfx/gfx.h>
+#include <gfx/port.h>
+#include <sdl-1.2/gfx/private.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+int quad_from_sprite(const struct gfx_sprite *const s, struct quad *const q)
+{
+ q->s = s->s;
+ q->s_x = s->s_x;
+ q->u0 = q->u2 = s->u;
+ q->v0 = q->v1 = s->v;
+ q->u1 = q->u3 = s->u + s->w - 1;
+ q->v2 = q->v3 = s->v + s->h - 1;
+ q->w = s->w;
+ q->h = s->h;
+ return 0;
+}
+
+void quad_sort(struct quad *const q)
+{
+ const bool xflip = q->x0 > q->x1;
+
+ SDL_Rect r =
+ {
+ .x = xflip ? q->x1 : q->x0,
+ .y = q->y0
+ };
+
+ const short w = q->u1 - q->u0 + 1, h = q->v2 - q->v0 + 1;
+
+ SDL_Rect clip =
+ {
+ .x = xflip ? q->w - q->u0 - w: q->u0,
+ .y = q->v0,
+ .w = w,
+ .h = h
+ };
+
+ SDL_Surface *const s = xflip ? q->s_x : q->s;
+
+ if (SDL_BlitSurface(s, &clip, gfx_screen(), &r))
+ fprintf(stderr, "SDL_BlitSurface: %s\n", SDL_GetError());
+}
diff --git a/src/gfx/sdl-1.2/src/rect.c b/src/gfx/sdl-1.2/src/rect.c
new file mode 100644
index 0000000..57a2ab3
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/rect.c
@@ -0,0 +1,35 @@
+#include <gfx/gfx.h>
+#include <gfx/port.h>
+#include <sdl-1.2/gfx/private.h>
+#include <SDL.h>
+
+void gfx_rect_sort(struct gfx_rect *const r)
+{
+ SDL_Rect rct =
+ {
+ .x = r->x,
+ .y = r->y,
+ .w = r->w,
+ .h = r->h
+ };
+
+ SDL_Surface *const screen = gfx_screen();
+ const Uint32 map = SDL_MapRGB(screen->format, r->r, r->g, r->b);
+
+ if (SDL_FillRect(screen, &rct, map))
+ {
+ fprintf(stderr, "SDL_FillRect: %s\n", SDL_GetError());
+ return;
+ }
+}
+
+void gfx_rect_init(struct gfx_rect *const r)
+{
+ *r = (const struct gfx_rect){0};
+}
+
+void semitrans_rect_init(struct gfx_rect *const r)
+{
+ gfx_rect_init(r);
+ r->stp = true;
+}
diff --git a/src/gfx/sdl-1.2/src/sprite.c b/src/gfx/sdl-1.2/src/sprite.c
new file mode 100644
index 0000000..086e2b5
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/sprite.c
@@ -0,0 +1,143 @@
+#include <gfx/gfx.h>
+#include <gfx/port.h>
+#include <header.h>
+#include <sdl-1.2/gfx/private.h>
+#include <SDL.h>
+#include <SDL_rotozoom.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+void sprite_free(struct gfx_sprite *const s)
+{
+ if (s && s->s)
+ SDL_FreeSurface(s->s);
+}
+
+int sprite_clone(const struct gfx_sprite *const src, struct gfx_sprite *const dst)
+{
+ *dst = *src;
+ return 0;
+}
+
+static int set_transparent(SDL_Surface *const s)
+{
+ /* Magenta as transparent. */
+ const Uint32 map = SDL_MapRGB(s->format, 255, 0, 255);
+ const int ret = SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, map);
+
+ if (ret)
+ fprintf(stderr, "SDL_SetColorKey: %s\n", SDL_GetError());
+
+ return ret;
+}
+
+int sprite_screen_resize_ev(struct gfx_sprite *const s)
+{
+ int ret = -1;
+ SDL_Surface *const old = s->s, *const old_x = s->s_x;
+
+ if (s->transparent && (set_transparent(old) || set_transparent(old_x)))
+ goto end;
+ else if (!(s->s = SDL_DisplayFormat(old)))
+ {
+ fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError());
+ goto end;
+ }
+ else if (!(s->s_x = SDL_DisplayFormat(old_x)))
+ {
+ fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError());
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+ SDL_FreeSurface(old);
+ SDL_FreeSurface(old_x);
+ return ret;
+}
+
+static int load_header(struct gfx_sprite *const s, FILE *const f)
+{
+ if (header_load_bool(f, "transparent", &s->transparent))
+ return -1;
+
+ return 0;
+}
+
+static int load_bitmap(struct gfx_sprite *const s, FILE *const f)
+{
+ int ret = -1;
+ SDL_RWops *ops = NULL;
+ SDL_Surface *ts = NULL, *zs = NULL;
+
+ if (!(ops = SDL_RWFromFP(f, 0)))
+ {
+ fprintf(stderr, "SDL_RWFromFP: %s\n", SDL_GetError());
+ goto end;
+ }
+ else if (!(ts = SDL_LoadBMP_RW(ops, 0)))
+ {
+ fprintf(stderr, "SDL_LoadBMP_RW: %s\n", SDL_GetError());
+ goto end;
+ }
+ else if (!(zs = zoomSurface(ts, -1, 1, 0)))
+ {
+ fprintf(stderr, "zoomSurface: %s\n", SDL_GetError());
+ goto end;
+ }
+ else if (s->transparent && (set_transparent(ts) || set_transparent(zs)))
+ goto end;
+ else if (!(s->s = SDL_DisplayFormat(ts)))
+ {
+ fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError());
+ goto end;
+ }
+ else if (!(s->s_x = SDL_DisplayFormat(zs)))
+ {
+ fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError());
+ goto end;
+ }
+
+ gfx_register_sprite(s);
+ s->w = ts->w;
+ s->h = ts->h;
+ ret = 0;
+
+end:
+ SDL_FreeRW(ops);
+ SDL_FreeSurface(ts);
+ SDL_FreeSurface(zs);
+ return ret;
+}
+
+int sprite_from_fp(struct gfx_sprite *const s, FILE *const f)
+{
+ *s = (const struct gfx_sprite){0};
+
+ if (load_header(s, f) || load_bitmap(s, f))
+ return -1;
+
+ return 0;
+}
+
+void sprite_sort(struct gfx_sprite *const s)
+{
+ SDL_Rect r =
+ {
+ .x = s->x,
+ .y = s->y
+ };
+
+ SDL_Rect clip =
+ {
+ .x = s->u,
+ .y = s->v,
+ .w = s->w,
+ .h = s->h
+ };
+
+ if (SDL_BlitSurface(s->s, &clip, gfx_screen(), &r))
+ fprintf(stderr, "SDL_BlitSurface: %s\n", SDL_GetError());
+}
diff --git a/src/gfx/src/CMakeLists.txt b/src/gfx/src/CMakeLists.txt
new file mode 100644
index 0000000..3b3cac2
--- /dev/null
+++ b/src/gfx/src/CMakeLists.txt
@@ -0,0 +1,19 @@
+# wanix, a Unix-like operating system for WebAssembly applications.
+# Copyright (C) 2025 Xavier Del Campo Romero
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+target_sources(gfx PRIVATE
+ dim.c
+)
diff --git a/src/gfx/src/dim.c b/src/gfx/src/dim.c
new file mode 100644
index 0000000..674da9c
--- /dev/null
+++ b/src/gfx/src/dim.c
@@ -0,0 +1,21 @@
+/*
+ * wanix, a Unix-like operating system for WebAssembly applications.
+ * Copyright (C) 2025 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gfx/private.h>
+
+int screen_w = SCREEN_W, screen_h = SCREEN_H;