tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE
commit 3b617a94f29bf2bb6bd66598363ffd3aeb08f890
parent 68a400de6768d12d6b8145c8813873fa8243d77c
Author: Connor Thomson <connor@gnufault.org>
Date: Mon, 2 Nov 2026 16:40:01 -0800
Rename and do a bunch of stuff
Diffstat:
55 files changed, 3777 insertions(+), 3603 deletions(-)
diff --git a/engine.mk b/engine.mk
@@ -21,8 +21,8 @@ ENGINE_DIR := $(ENGINE)
ENGINE_SRCS := $(shell find $(ENGINE_DIR)/src -type f -name '*.c' | sort)
ENGINE_OBJS := $(patsubst $(ENGINE_DIR)/src/%.c, $(BUILD_DIR)/engine/src/%.o, $(ENGINE_SRCS))
-CONFIG_HEADER := $(BUILD_DIR)/engine/include/ta_config.h
-FILES_HEADER := $(BUILD_DIR)/engine/include/ta_files.h
+CONFIG_HEADER := $(BUILD_DIR)/engine/include/config.h
+FILES_HEADER := $(BUILD_DIR)/engine/include/files.h
ASSETS := $(shell find $(ENGINE_DIR)/font $(ENGINE_DIR)/images $(ENGINE_DIR)/files -type f -not -name 'LICENSE' 2>/dev/null | sort)
asset_sym = $(shell printf '%s' 'ta_file_$(notdir $(basename $(1)))' | sed 's/[^a-zA-Z0-9_]/_/g')
@@ -46,7 +46,7 @@ $(BUILD_DIR)/engine/assets/$(notdir $(basename $(1))).c: $(1)
endef
$(foreach a,$(ASSETS),$(eval $(call ASSET_RULE,$(a))))
-$(CONFIG_HEADER): $(ENGINE_DIR)/include/ta_config.h.in
+$(CONFIG_HEADER): $(ENGINE_DIR)/include/config.h.in
mkdir -p $(@D)
sed 's/\$$BITS/$(BITS)/g' $< > $@
diff --git a/include/abi.h b/include/abi.h
@@ -0,0 +1,26 @@
+#ifndef TA_ABI_H
+#define TA_ABI_H
+
+#define TA_SYS_EXIT 0
+#define TA_SYS_READY 1
+#define TA_SYS_RETURN 2
+#define TA_SYS_DISPLAY_WIDTH 3
+#define TA_SYS_DISPLAY_HEIGHT 4
+#define TA_SYS_DISPLAY_SPRITE_MODE 5
+#define TA_SYS_KEYBOARD_IS_DOWN 6
+#define TA_SYS_IMAGE_INIT 7
+#define TA_SYS_IMAGE_DRAW 8
+#define TA_SYS_IMAGE_GET_SIZE 9
+#define TA_SYS_TEXT_CREATE 10
+#define TA_SYS_TEXT_WIDTH 11
+#define TA_SYS_TEXT_HEIGHT 12
+#define TA_SYS_TEXT_DRAW 13
+#define TA_SYS_TEXT_DESTROY 14
+#define TA_SYS_SCREEN_REGISTER 15
+#define TA_SYS_SCREEN_SWITCH 16
+#define TA_SYS_SHAPES_PIXEL 17
+#define TA_SYS_SHAPES_RECTANGLE 18
+#define TA_SYS_SHAPES_TRIANGLE 19
+#define TA_SYS_SHAPES_OVAL 20
+
+#endif // TA_ABI_H
diff --git a/include/api.h b/include/api.h
@@ -0,0 +1,29 @@
+#ifndef TA_API_H
+#define TA_API_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "fb.h"
+#include "keyboard.h"
+#include "title.h"
+#include "fs.h"
+
+// Must be called before ta_update()
+void ta_main_init(void);
+
+// Must be called every frame after ta_init()
+bool ta_main_update(void);
+
+// File system
+ta_fs_file ta_read_file(const char *path);
+void ta_close_file(ta_fs_file *file);
+
+void ta_exit(int status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TA_API_H
diff --git a/include/ta_config.h.in b/include/config.h.in
diff --git a/include/display.h b/include/display.h
@@ -0,0 +1,47 @@
+#ifndef TA_DISPLAY_H
+#define TA_DISPLAY_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+typedef enum {
+ TA_DISPLAY_PIXEL_FORMAT_8,
+ TA_DISPLAY_PIXEL_FORMAT_16,
+ TA_DISPLAY_PIXEL_FORMAT_32
+} ta_display_pixel_format;
+
+typedef struct {
+ uint16_t width, height;
+ uint8_t bpp;
+ size_t size;
+ void *buffers[2];
+ ta_display_pixel_format format;
+ bool sprite_mode;
+} ta_display_t;
+
+typedef struct {
+ uint16_t x, y;
+ uint16_t width, height;
+ uint16_t draw_width, draw_height;
+ ta_display_pixel_format format;
+ uint8_t pixels[];
+} ta_display_sprite;
+
+typedef struct {
+ ta_display_sprite **sprites;
+ uint8_t count;
+ uint8_t capacity;
+} ta_display_sprites_t;
+
+extern ta_display_t *ta_display;
+extern ta_display_sprites_t *ta_display_sprites;
+
+bool ta_display_init(void);
+void ta_display_deinit(void);
+ta_display_sprite *ta_display_sprite_create(uint16_t width, uint16_t height);
+bool ta_display_sprite_add(ta_display_sprite *sprite);
+void ta_display_sprite_free(ta_display_sprite *sprite);
+void ta_display_sprites_clear(void);
+
+#endif // TA_DISPLAY_H
diff --git a/include/fb.h b/include/fb.h
@@ -0,0 +1,14 @@
+#ifndef TA_FB_H
+#define TA_FB_H
+
+#include "display.h"
+
+typedef ta_display_t ta_fbs;
+
+#define ta_fb (*ta_display)
+
+size_t ta_fb_init(ta_fbs *fb);
+void ta_fb_swap(ta_fbs *fb);
+void ta_fb_clear(ta_fbs *fb);
+
+#endif
diff --git a/include/fixed.h b/include/fixed.h
@@ -0,0 +1,9 @@
+#ifndef TA_FIXED_H
+#define TA_FIXED_H
+
+typedef uint32_t ta_fixed;
+
+#define TA_FIXED_ONE 65536u
+#define TA_FIXED(value) ((ta_fixed)((value) * 65536.0 + 0.5))
+
+#endif // TA_FIXED_H
diff --git a/include/fs.h b/include/fs.h
@@ -0,0 +1,11 @@
+#ifndef TA_FS_H
+#define TA_FS_H
+
+#include <stddef.h>
+
+typedef struct {
+ char *data;
+ size_t size;
+} ta_fs_file;
+
+#endif // TA_FS_H
diff --git a/include/image.h b/include/image.h
@@ -0,0 +1,13 @@
+#ifndef TA_IMAGE_H
+#define TA_IMAGE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+bool ta_image_draw(const uint8_t *data, uint32_t length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, uint16_t frame);
+bool ta_image_render(const uint8_t *data, uint32_t length, uint16_t frame, uint32_t width, uint32_t height, uint32_t *pixels);
+bool ta_image_init(const uint8_t *data, uint32_t length);
+bool ta_image_get_size(const uint8_t *data, uint32_t length, uint32_t *width, uint32_t *height);
+void ta_image_release(void);
+
+#endif
diff --git a/include/keyboard.h b/include/keyboard.h
@@ -0,0 +1,21 @@
+#ifndef TA_KEYBOARD_H
+#define TA_KEYBOARD_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "keys.h"
+
+#define TA_KEYBOARD_MAX_KEYS_DOWN 5
+
+typedef struct {
+ uint8_t keys_down[TA_KEYBOARD_MAX_KEYS_DOWN];
+} ta_keyboards;
+
+extern ta_keyboards ta_keyboard;
+
+bool ta_keyboard_is_down(ta_keyboard_keys key);
+void ta_keyboard_key_down(ta_keyboard_keys key);
+void ta_keyboard_key_up(ta_keyboard_keys key);
+void ta_keyboard_clear(void);
+
+#endif // TA_KEYBOARD_H
diff --git a/include/ta_keys.h b/include/keys.h
diff --git a/include/ta_main.h b/include/main.h
diff --git a/include/scene.h b/include/scene.h
@@ -0,0 +1,17 @@
+#ifndef TA_SCENE_H
+#define TA_SCENE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "fixed.h"
+#include "text.h"
+
+void ta_scene_begin(void);
+bool ta_scene_end(void);
+void ta_scene_invalidate(void);
+bool ta_scene_image(const uint8_t *data, uint32_t length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, uint16_t frame);
+bool ta_scene_text(ta_text *text, uint32_t x, uint32_t y);
+bool ta_scene_shape(uint32_t type, const uint32_t *args, uint32_t color);
+bool ta_scene_shape(uint32_t type, const uint32_t *args, uint32_t color);
+
+#endif // TA_SCENE_H
diff --git a/include/screen.h b/include/screen.h
@@ -0,0 +1,13 @@
+#ifndef TA_SCREEN_H
+#define TA_SCREEN_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+void ta_screen_init(void);
+bool ta_screen_register(uint32_t id, uint32_t init, uint32_t update, uint32_t destroy);
+bool ta_screen_switch(uint32_t id);
+bool ta_screen_update(void);
+void ta_screen_destroy(void);
+
+#endif // TA_SCREEN_H
diff --git a/include/shapes.h b/include/shapes.h
@@ -0,0 +1,11 @@
+#ifndef TA_SHAPES_H
+#define TA_SHAPES_H
+
+#include <stdint.h>
+
+void ta_shapes_draw_pixel(uint32_t x, uint32_t y, uint32_t color);
+void ta_shapes_draw_rectangle(uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t color);
+void ta_shapes_draw_triangle(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2, uint32_t x3, uint32_t y3, uint32_t color);
+void ta_shapes_draw_oval(uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t color);
+
+#endif // TA_SHAPES_H
diff --git a/include/syscall.h b/include/syscall.h
@@ -0,0 +1,9 @@
+#ifndef TA_SYSCALL_H
+#define TA_SYSCALL_H
+
+#include "vm.h"
+
+ta_vm_status ta_syscall(ta_vm *vm);
+void ta_syscall_reset(void);
+
+#endif // TA_SYSCALL_H
diff --git a/include/ta_abi.h b/include/ta_abi.h
@@ -1,22 +0,0 @@
-#ifndef TA_ABI_H
-#define TA_ABI_H
-
-#define TA_SYS_EXIT 0
-#define TA_SYS_READY 1
-#define TA_SYS_RETURN 2
-#define TA_SYS_DISPLAY_WIDTH 3
-#define TA_SYS_DISPLAY_HEIGHT 4
-#define TA_SYS_DISPLAY_SPRITE_MODE 5
-#define TA_SYS_KEYBOARD_IS_DOWN 10
-#define TA_SYS_IMAGE_INIT 11
-#define TA_SYS_IMAGE_DRAW 12
-#define TA_SYS_IMAGE_GET_SIZE 13
-#define TA_SYS_TEXT_CREATE 14
-#define TA_SYS_TEXT_WIDTH 15
-#define TA_SYS_TEXT_HEIGHT 16
-#define TA_SYS_TEXT_DRAW 17
-#define TA_SYS_TEXT_DESTROY 18
-#define TA_SYS_SCREEN_REGISTER 19
-#define TA_SYS_SCREEN_SWITCH 20
-
-#endif // TA_ABI_H
diff --git a/include/ta_api.h b/include/ta_api.h
@@ -1,36 +0,0 @@
-#ifndef TA_API_H
-#define TA_API_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ta_config.h"
-#include "ta_fb.h"
-#include "ta_keyboard.h"
-#include "ta_title.h"
-#include "ta_types.h"
-#include "ta_fs.h"
-
-// Must be called before ta_update()
-void ta_main_init(void);
-
-// Must be called every frame after ta_init()
-ta_bool ta_main_update(void);
-
-// Memory allocation
-void *ta_malloc(ta_size size);
-void *ta_realloc(void *ptr, ta_size new_size);
-void ta_free(void *ptr);
-
-// File system
-ta_fs_file ta_read_file(const char *path);
-void ta_close_file(ta_fs_file *file);
-
-void ta_exit(int status);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // TA_API_H
diff --git a/include/ta_display.h b/include/ta_display.h
@@ -1,45 +0,0 @@
-#ifndef TA_DISPLAY_H
-#define TA_DISPLAY_H
-
-#include "ta_types.h"
-
-typedef enum {
- TA_DISPLAY_PIXEL_FORMAT_8,
- TA_DISPLAY_PIXEL_FORMAT_16,
- TA_DISPLAY_PIXEL_FORMAT_32
-} ta_display_pixel_format;
-
-typedef struct {
- ta_uint16 width, height;
- ta_uint8 bpp;
- ta_size size;
- void *buffers[2];
- ta_display_pixel_format format;
- ta_bool sprite_mode;
-} ta_display_t;
-
-typedef struct {
- ta_uint16 x, y;
- ta_uint16 width, height;
- ta_uint16 draw_width, draw_height;
- ta_display_pixel_format format;
- ta_uint8 pixels[];
-} ta_display_sprite;
-
-typedef struct {
- ta_display_sprite **sprites;
- ta_uint8 count;
- ta_uint8 capacity;
-} ta_display_sprites_t;
-
-extern ta_display_t *ta_display;
-extern ta_display_sprites_t *ta_display_sprites;
-
-ta_bool ta_display_init(void);
-void ta_display_deinit(void);
-ta_display_sprite *ta_display_sprite_create(ta_uint16 width, ta_uint16 height);
-ta_bool ta_display_sprite_add(ta_display_sprite *sprite);
-void ta_display_sprite_free(ta_display_sprite *sprite);
-void ta_display_sprites_clear(void);
-
-#endif // TA_DISPLAY_H
diff --git a/include/ta_fb.h b/include/ta_fb.h
@@ -1,14 +0,0 @@
-#ifndef TA_FB_H
-#define TA_FB_H
-
-#include "ta_display.h"
-
-typedef ta_display_t ta_fbs;
-
-#define ta_fb (*ta_display)
-
-ta_size ta_fb_init(ta_fbs *fb);
-void ta_fb_swap(ta_fbs *fb);
-void ta_fb_clear(ta_fbs *fb);
-
-#endif
diff --git a/include/ta_fs.h b/include/ta_fs.h
@@ -1,11 +0,0 @@
-#ifndef TA_FS_H
-#define TA_FS_H
-
-#include "ta_types.h"
-
-typedef struct {
- char *data;
- ta_size size;
-} ta_fs_file;
-
-#endif // TA_FS_H
diff --git a/include/ta_image.h b/include/ta_image.h
@@ -1,12 +0,0 @@
-#ifndef TA_IMAGE_H
-#define TA_IMAGE_H
-
-#include "ta_types.h"
-
-ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, ta_uint16 frame);
-ta_bool ta_image_render(const ta_uint8 *data, ta_uint32 length, ta_uint16 frame, ta_uint32 width, ta_uint32 height, ta_uint32 *pixels);
-ta_bool ta_image_init(const ta_uint8 *data, ta_uint32 length);
-ta_bool ta_image_get_size(const ta_uint8 *data, ta_uint32 length, ta_uint32 *width, ta_uint32 *height);
-void ta_image_release(void);
-
-#endif
diff --git a/include/ta_keyboard.h b/include/ta_keyboard.h
@@ -1,20 +0,0 @@
-#ifndef TA_KEYBOARD_H
-#define TA_KEYBOARD_H
-
-#include "ta_keys.h"
-#include "ta_types.h"
-
-#define TA_KEYBOARD_MAX_KEYS_DOWN 5
-
-typedef struct {
- ta_uint8 keys_down[TA_KEYBOARD_MAX_KEYS_DOWN];
-} ta_keyboards;
-
-extern ta_keyboards ta_keyboard;
-
-ta_bool ta_keyboard_is_down(ta_keyboard_keys key);
-void ta_keyboard_key_down(ta_keyboard_keys key);
-void ta_keyboard_key_up(ta_keyboard_keys key);
-void ta_keyboard_clear(void);
-
-#endif // TA_KEYBOARD_H
diff --git a/include/ta_scene.h b/include/ta_scene.h
@@ -1,13 +0,0 @@
-#ifndef TA_SCENE_H
-#define TA_SCENE_H
-
-#include "ta_text.h"
-#include "ta_types.h"
-
-void ta_scene_begin(void);
-ta_bool ta_scene_end(void);
-void ta_scene_invalidate(void);
-ta_bool ta_scene_image(const ta_uint8 *data, ta_uint32 length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, ta_uint16 frame);
-ta_bool ta_scene_text(ta_text *text, ta_uint32 x, ta_uint32 y);
-
-#endif // TA_SCENE_H
diff --git a/include/ta_screen.h b/include/ta_screen.h
@@ -1,12 +0,0 @@
-#ifndef TA_SCREEN_H
-#define TA_SCREEN_H
-
-#include "ta_types.h"
-
-void ta_screen_init(void);
-ta_bool ta_screen_register(ta_uint32 id, ta_uint32 init, ta_uint32 update, ta_uint32 destroy);
-ta_bool ta_screen_switch(ta_uint32 id);
-ta_bool ta_screen_update(void);
-void ta_screen_destroy(void);
-
-#endif // TA_SCREEN_H
diff --git a/include/ta_syscall.h b/include/ta_syscall.h
@@ -1,10 +0,0 @@
-#ifndef TA_SYSCALL_H
-#define TA_SYSCALL_H
-
-#include "ta_types.h"
-#include "ta_vm.h"
-
-ta_vm_status ta_syscall(ta_vm *vm);
-void ta_syscall_reset(void);
-
-#endif // TA_SYSCALL_H
diff --git a/include/ta_text.h b/include/ta_text.h
@@ -1,22 +0,0 @@
-#ifndef TA_TEXT_H
-#define TA_TEXT_H
-
-#include "ta_display.h"
-#include "ta_types.h"
-
-typedef struct {
- const char *text;
- ta_uint32 x, y;
- ta_uint8 size;
- ta_uint8 red, green, blue;
- ta_uint8 border_width;
- ta_uint8 border_red, border_green, border_blue;
- ta_uint32 width, height;
- ta_display_sprite *sprite;
-} ta_text;
-
-void ta_text_rasterize(ta_text *text);
-void ta_text_draw(ta_text *text);
-void ta_text_destroy(ta_text *text);
-
-#endif // TA_TEXT_H
diff --git a/include/ta_types.h b/include/ta_types.h
@@ -1,40 +0,0 @@
-#ifndef TA_TYPES_H
-#define TA_TYPES_H
-
-#include "ta_config.h"
-
-#define TA_NULL ((void*)0)
-
-typedef unsigned char ta_uint8;
-typedef unsigned short ta_uint16;
-typedef unsigned int ta_uint32;
-typedef unsigned long long ta_uint64;
-
-typedef signed char ta_int8;
-typedef short ta_int16;
-typedef int ta_int32;
-typedef long long ta_int64;
-
-#if TA_BITS == 64
-typedef ta_uint64 ta_size;
-#elif TA_BITS == 32
-typedef ta_uint32 ta_size;
-#else
-#error "TA_BITS is not set!"
-#endif
-
-#ifdef __cplusplus
-typedef bool ta_bool;
-#else
-typedef _Bool ta_bool;
-#endif
-
-#define ta_true 1
-#define ta_false 0
-
-typedef ta_uint32 ta_fixed;
-
-#define TA_FIXED_ONE 65536u
-#define TA_FIXED(value) ((ta_fixed)((value) * 65536.0 + 0.5))
-
-#endif // TA_STDINT_H
diff --git a/include/ta_vm.h b/include/ta_vm.h
@@ -1,65 +0,0 @@
-#ifndef TA_VM_H
-#define TA_VM_H
-
-#include "ta_types.h"
-
-typedef enum {
- TA_VM_RUNNING,
- TA_VM_HALTED,
- TA_VM_YIELDED,
- TA_VM_FAULT_ILLEGAL,
- TA_VM_FAULT_MEMORY,
- TA_VM_FAULT_MISALIGNED,
- TA_VM_FAULT_EBREAK
-} ta_vm_status;
-
-typedef struct ta_vm ta_vm;
-typedef ta_vm_status (*ta_vm_ecall)(ta_vm *vm, void *user);
-
-struct ta_vm {
- ta_uint32 regs[32];
- ta_uint32 pc;
- ta_uint8 *mem;
- ta_uint32 mem_size;
- ta_vm_ecall ecall;
- void *user;
- ta_uint32 steps;
-};
-
-#define TA_VM_REG_RA 1
-#define TA_VM_REG_SP 2
-#define TA_VM_REG_A0 10
-#define TA_VM_REG_A1 11
-#define TA_VM_REG_A2 12
-#define TA_VM_REG_A3 13
-#define TA_VM_REG_A4 14
-#define TA_VM_REG_A5 15
-#define TA_VM_REG_A6 16
-#define TA_VM_REG_A7 17
-
-void ta_vm_init(ta_vm *vm, ta_uint8 *mem, ta_uint32 mem_size, ta_vm_ecall ecall, void *user);
-void ta_vm_reset(ta_vm *vm, ta_uint32 entry, ta_uint32 stack_top);
-ta_vm_status ta_vm_run(ta_vm *vm, ta_uint32 max_steps);
-ta_bool ta_vm_read8(const ta_vm *vm, ta_uint32 addr, ta_uint8 *out);
-ta_bool ta_vm_read32(const ta_vm *vm, ta_uint32 addr, ta_uint32 *out);
-ta_bool ta_vm_write32(ta_vm *vm, ta_uint32 addr, ta_uint32 value);
-void *ta_vm_translate_pointer(const ta_vm *vm, ta_uint32 addr, ta_uint32 size);
-const char *ta_vm_cstr(const ta_vm *vm, ta_uint32 addr);
-
-#ifndef TA_VM_RAM_SIZE
-#define TA_VM_RAM_SIZE (256u * 1024u)
-#endif
-
-#ifndef TA_VM_STEPS_PER_CALL
-#define TA_VM_STEPS_PER_CALL 200000u
-#endif
-
-extern ta_bool ta_vm_running;
-
-ta_bool ta_vm_load(const ta_uint8 *binary, ta_size size);
-ta_bool ta_vm_load_file(const char *path);
-ta_bool ta_vm_call(ta_uint32 function, ta_uint32 arg0, ta_uint32 arg1, ta_uint32 arg2, ta_uint32 *result);
-ta_int32 ta_vm_exit_code(void);
-void ta_vm_unload(void);
-
-#endif // TA_VM_H
diff --git a/include/text.h b/include/text.h
@@ -0,0 +1,22 @@
+#ifndef TA_TEXT_H
+#define TA_TEXT_H
+
+#include <stdint.h>
+#include "display.h"
+
+typedef struct {
+ const char *text;
+ uint32_t x, y;
+ uint8_t size;
+ uint8_t red, green, blue;
+ uint8_t border_width;
+ uint8_t border_red, border_green, border_blue;
+ uint32_t width, height;
+ ta_display_sprite *sprite;
+} ta_text;
+
+void ta_text_rasterize(ta_text *text);
+void ta_text_draw(ta_text *text);
+void ta_text_destroy(ta_text *text);
+
+#endif // TA_TEXT_H
diff --git a/include/ta_title.h b/include/title.h
diff --git a/include/vm.h b/include/vm.h
@@ -0,0 +1,66 @@
+#ifndef TA_VM_H
+#define TA_VM_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef enum {
+ TA_VM_RUNNING,
+ TA_VM_HALTED,
+ TA_VM_YIELDED,
+ TA_VM_FAULT_ILLEGAL,
+ TA_VM_FAULT_MEMORY,
+ TA_VM_FAULT_MISALIGNED,
+ TA_VM_FAULT_EBREAK
+} ta_vm_status;
+
+typedef struct ta_vm ta_vm;
+typedef ta_vm_status (*ta_vm_ecall)(ta_vm *vm, void *user);
+
+struct ta_vm {
+ uint32_t regs[32];
+ uint32_t pc;
+ uint8_t *mem;
+ uint32_t mem_size;
+ ta_vm_ecall ecall;
+ void *user;
+ uint32_t steps;
+};
+
+#define TA_VM_REG_RA 1
+#define TA_VM_REG_SP 2
+#define TA_VM_REG_A0 10
+#define TA_VM_REG_A1 11
+#define TA_VM_REG_A2 12
+#define TA_VM_REG_A3 13
+#define TA_VM_REG_A4 14
+#define TA_VM_REG_A5 15
+#define TA_VM_REG_A6 16
+#define TA_VM_REG_A7 17
+
+void ta_vm_init(ta_vm *vm, uint8_t *mem, uint32_t mem_size, ta_vm_ecall ecall, void *user);
+void ta_vm_reset(ta_vm *vm, uint32_t entry, uint32_t stack_top);
+ta_vm_status ta_vm_run(ta_vm *vm, uint32_t max_steps);
+bool ta_vm_read8(const ta_vm *vm, uint32_t addr, uint8_t *out);
+bool ta_vm_read32(const ta_vm *vm, uint32_t addr, uint32_t *out);
+bool ta_vm_write32(ta_vm *vm, uint32_t addr, uint32_t value);
+void *ta_vm_translate_pointer(const ta_vm *vm, uint32_t addr, uint32_t size);
+const char *ta_vm_cstr(const ta_vm *vm, uint32_t addr);
+
+#ifndef TA_VM_RAM_SIZE
+#define TA_VM_RAM_SIZE (256u * 1024u)
+#endif
+
+#ifndef TA_VM_STEPS_PER_CALL
+#define TA_VM_STEPS_PER_CALL 200000u
+#endif
+
+extern bool ta_vm_running;
+
+bool ta_vm_load(const uint8_t *binary, size_t size);
+bool ta_vm_load_file(const char *path);
+bool ta_vm_call(uint32_t function, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t *result);
+int32_t ta_vm_exit_code(void);
+void ta_vm_unload(void);
+
+#endif // TA_VM_H
diff --git a/src/display.c b/src/display.c
@@ -0,0 +1,136 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include "display.h"
+
+static ta_display_t default_display;
+ta_display_t *ta_display = &default_display;
+ta_display_sprites_t *ta_display_sprites;
+
+bool ta_display_init(void) {
+ if (ta_display_sprites) {
+ return true;
+ }
+
+ ta_display_sprites = malloc(sizeof(*ta_display_sprites));
+ if (!ta_display_sprites) {
+ return false;
+ }
+
+ ta_display_sprites->count = 0;
+ ta_display_sprites->capacity = 0;
+ ta_display_sprites->sprites = NULL;
+
+ return true;
+}
+
+void ta_display_deinit(void) {
+ if (ta_display_sprites) {
+ for (uint8_t i = 0; i < ta_display_sprites->count; i++) {
+ free(ta_display_sprites->sprites[i]);
+ }
+
+ free(ta_display_sprites->sprites);
+ free(ta_display_sprites);
+ ta_display_sprites = NULL;
+ }
+
+ free(ta_display->buffers[0]);
+ free(ta_display->buffers[1]);
+ ta_display->buffers[0] = NULL;
+ ta_display->buffers[1] = NULL;
+ ta_display->size = 0;
+}
+
+ta_display_sprite *ta_display_sprite_create(uint16_t width, uint16_t height) {
+ if (width == 0 || height == 0) {
+ return NULL;
+ }
+
+ size_t max_size = (size_t)-1;
+ size_t pixel_count = (size_t)width * height;
+
+ if (pixel_count > (max_size - sizeof(ta_display_sprite)) / sizeof(uint32_t)) {
+ return NULL;
+ }
+
+ ta_display_sprite *sprite = malloc(sizeof(*sprite) + pixel_count * sizeof(uint32_t));
+ if (!sprite) {
+ return NULL;
+ }
+
+ sprite->x = 0;
+ sprite->y = 0;
+ sprite->width = width;
+ sprite->height = height;
+ sprite->draw_width = width;
+ sprite->draw_height = height;
+ sprite->format = TA_DISPLAY_PIXEL_FORMAT_32;
+
+ return sprite;
+}
+
+bool ta_display_sprite_add(ta_display_sprite *sprite) {
+ if (!sprite || !ta_display_sprites) {
+ return false;
+ }
+
+ ta_display_sprites_t *list = ta_display_sprites;
+
+ if (list->count == list->capacity) {
+ if (list->capacity == 255) {
+ return false;
+ }
+
+ uint16_t new_capacity = list->capacity ? (uint16_t)list->capacity * 2 : 8;
+ if (new_capacity > 255) {
+ new_capacity = 255;
+ }
+
+ ta_display_sprite **new_sprites = malloc(new_capacity * sizeof(ta_display_sprite *));
+ if (!new_sprites) {
+ return false;
+ }
+
+ for (uint8_t i = 0; i < list->count; i++) {
+ new_sprites[i] = list->sprites[i];
+ }
+
+ free(list->sprites);
+ list->sprites = new_sprites;
+ list->capacity = (uint8_t)new_capacity;
+ }
+
+ list->sprites[list->count++] = sprite;
+ return true;
+}
+
+void ta_display_sprite_free(ta_display_sprite *sprite) {
+ if (!sprite) {
+ return;
+ }
+
+ if (ta_display_sprites) {
+ for (uint8_t i = 0; i < ta_display_sprites->count; i++) {
+ if (ta_display_sprites->sprites[i] == sprite) {
+ for (uint8_t j = i; j < ta_display_sprites->count - 1; j++) {
+ ta_display_sprites->sprites[j] = ta_display_sprites->sprites[j + 1];
+ }
+
+ ta_display_sprites->count--;
+ break;
+ }
+ }
+ }
+
+ free(sprite);
+}
+
+void ta_display_sprites_clear(void) {
+ if (!ta_display_sprites) {
+ return;
+ }
+
+ while (ta_display_sprites->count != 0) {
+ ta_display_sprite_free(ta_display_sprites->sprites[ta_display_sprites->count - 1]);
+ }
+}
diff --git a/src/fb.c b/src/fb.c
@@ -0,0 +1,82 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include "fb.h"
+
+size_t ta_fb_init(ta_fbs *fb) {
+ if (fb == 0 || fb->width == 0 || fb->height == 0 || fb->bpp == 0 || fb->bpp % 8 != 0) {
+ return 0;
+ }
+
+ size_t bytes_per_pixel = fb->bpp / 8;
+ size_t max_size = (size_t)-1;
+ if (fb->width > max_size / bytes_per_pixel || fb->height > max_size / (fb->width * bytes_per_pixel)) {
+ return 0;
+ }
+
+ size_t size = fb->width * fb->height * bytes_per_pixel;
+ free(fb->buffers[0]);
+ free(fb->buffers[1]);
+ fb->buffers[0] = 0;
+ fb->buffers[1] = 0;
+
+ void *first = malloc(size);
+ if (first == 0) {
+ return 0;
+ }
+
+ void *second = malloc(size);
+ if (second == 0) {
+ free(first);
+ return 0;
+ }
+
+ fb->size = size;
+ fb->buffers[0] = first;
+ fb->buffers[1] = second;
+ if (fb->bpp == 8) {
+ fb->format = TA_DISPLAY_PIXEL_FORMAT_8;
+ } else if (fb->bpp == 16) {
+ fb->format = TA_DISPLAY_PIXEL_FORMAT_16;
+ } else {
+ fb->format = TA_DISPLAY_PIXEL_FORMAT_32;
+ }
+ return size;
+}
+
+void ta_fb_swap(ta_fbs *fb) {
+ if (!fb || !fb->buffers[0] || !fb->buffers[1]) {
+ return;
+ }
+
+ void *temp = fb->buffers[0];
+ fb->buffers[0] = fb->buffers[1];
+ fb->buffers[1] = temp;
+}
+
+void ta_fb_clear(ta_fbs *fb) {
+ if (!fb) {
+ return;
+ }
+
+ if (fb->sprite_mode) {
+ ta_display_sprites_clear();
+ return;
+ }
+
+ if (!fb->buffers[1] || fb->size == 0) {
+ return;
+ }
+
+ size_t *dest = (size_t *)fb->buffers[1];
+ size_t num_words = fb->size / sizeof(size_t);
+
+ for (size_t i = 0; i < num_words; i++) {
+ dest[i] = 0;
+ }
+
+ unsigned char *byte_dest = (unsigned char *)fb->buffers[1];
+ size_t handled_bytes = num_words * sizeof(size_t);
+ for (size_t i = handled_bytes; i < fb->size; i++) {
+ byte_dest[i] = 0;
+ }
+}
diff --git a/src/image.c b/src/image.c
@@ -0,0 +1,698 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "display.h"
+#include "fixed.h"
+
+#define TAIF_FLAG_8BPP 0x01
+#define TAIF_FLAG_TRANSPARENT 0x02
+#define TAIF_FLAG_ANIMATED 0x04
+#define TAIF_WINDOW_SIZE 4096
+
+typedef struct {
+ uint8_t window[TAIF_WINDOW_SIZE];
+ uint32_t stream_position;
+ uint32_t window_position;
+ uint32_t filled;
+ uint32_t match_distance;
+ uint32_t match_remaining;
+ uint8_t item_flags;
+ uint8_t flags_remaining;
+} ta_image_decoder;
+
+typedef struct ta_image_cache {
+ const uint8_t *data;
+ uint32_t length;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t frame_size;
+ uint16_t frame_count;
+ uint16_t palette_entries;
+ uint32_t frames_decoded;
+ uint8_t flags;
+ uint8_t palette[256][3];
+ const uint8_t *stream;
+ uint32_t stream_length;
+ ta_image_decoder decoder;
+ uint8_t *pixels;
+ uint8_t *scaled;
+ uint32_t scaled_width;
+ uint32_t scaled_height;
+ uint8_t scaled_bpp;
+ struct ta_image_cache *next;
+} ta_image_cache;
+
+static ta_image_cache *image_cache;
+
+static uint16_t read_le16(const uint8_t *data) {
+ return (uint16_t)((uint16_t)data[0] | ((uint16_t)data[1] << 8));
+}
+
+static bool parse_image(const uint8_t *data, uint32_t length, ta_image_cache *image) {
+ if (data == 0 || length < 11 || data[0] != 'T' || data[1] != 'A' || data[2] != 'I' || data[3] != 'F' || data[4] != 1) {
+ return false;
+ }
+
+ uint8_t flags = data[5];
+ if ((flags & 0xf8) != 0) {
+ return false;
+ }
+
+ uint32_t width = read_le16(data + 6);
+ uint32_t height = read_le16(data + 8);
+ uint32_t palette_entries = (uint32_t)data[10] + 1;
+ bool eight_bpp = (flags & TAIF_FLAG_8BPP) != 0;
+ bool transparent = (flags & TAIF_FLAG_TRANSPARENT) != 0;
+ uint32_t max_entries = eight_bpp ? (transparent ? 255 : 256) : (transparent ? 15 : 16);
+ uint32_t header_size = 11;
+ uint16_t frame_count = 1;
+
+ if (width == 0 || height == 0 || palette_entries > max_entries) {
+ return false;
+ }
+
+ if ((flags & TAIF_FLAG_ANIMATED) != 0) {
+ if (length < 14 || data[11] == 0) {
+ return false;
+ }
+
+ frame_count = read_le16(data + 12);
+ if (frame_count < 2) {
+ return false;
+ }
+
+ header_size = 14;
+ }
+
+ uint64_t stride = eight_bpp ? width : (width + 1) / 2;
+ uint64_t frame_size = stride * height;
+ uint64_t max_size = (uint64_t)(size_t)-1;
+ uint64_t palette_size = (uint64_t)palette_entries * 3;
+
+ if (frame_size == 0 || frame_size > max_size || frame_size > 0xffffffffu || header_size + palette_size > length) {
+ return false;
+ }
+
+ const uint8_t *palette = data + header_size;
+
+ image->pixels = (uint8_t *)malloc((size_t)frame_size);
+ if (image->pixels == 0) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < (uint32_t)frame_size; i++) {
+ image->pixels[i] = 0;
+ }
+
+ image->data = data;
+ image->length = length;
+ image->width = width;
+ image->height = height;
+ image->stride = (uint32_t)stride;
+ image->frame_size = (uint32_t)frame_size;
+ image->frame_count = frame_count;
+ image->palette_entries = (uint16_t)palette_entries;
+ image->flags = flags;
+ image->stream = palette + palette_size;
+ image->stream_length = length - (uint32_t)(header_size + palette_size);
+
+ for (uint32_t i = 0; i < palette_entries; i++) {
+ for (uint32_t channel = 0; channel < 3; channel++) {
+ image->palette[i][channel] = palette[i * 3 + channel];
+ }
+ }
+
+ return true;
+}
+
+static void decoder_reset(ta_image_cache *image) {
+ ta_image_decoder *decoder = &image->decoder;
+
+ decoder->stream_position = 0;
+ decoder->window_position = 0;
+ decoder->filled = 0;
+ decoder->match_distance = 0;
+ decoder->match_remaining = 0;
+ decoder->item_flags = 0;
+ decoder->flags_remaining = 0;
+
+ for (uint32_t i = 0; i < image->frame_size; i++) {
+ image->pixels[i] = 0;
+ }
+
+ free(image->scaled);
+
+ image->scaled = 0;
+ image->frames_decoded = 0;
+}
+
+static bool decode_next_frame(ta_image_cache *image) {
+ ta_image_decoder *decoder = &image->decoder;
+ const uint8_t *stream = image->stream;
+ uint32_t stream_length = image->stream_length;
+ uint32_t frame_size = image->frame_size;
+ uint32_t position = 0;
+
+ while (position < frame_size) {
+ uint8_t value;
+
+ if (decoder->match_remaining != 0) {
+ uint32_t source_position = (decoder->window_position + TAIF_WINDOW_SIZE - decoder->match_distance) & (TAIF_WINDOW_SIZE - 1);
+
+ value = decoder->window[source_position];
+ decoder->match_remaining--;
+ } else {
+ if (decoder->flags_remaining == 0) {
+ if (decoder->stream_position >= stream_length) {
+ return false;
+ }
+
+ decoder->item_flags = stream[decoder->stream_position++];
+ decoder->flags_remaining = 8;
+ }
+
+ bool literal = (decoder->item_flags & 1) != 0;
+
+ decoder->item_flags >>= 1;
+ decoder->flags_remaining--;
+
+ if (literal) {
+ if (decoder->stream_position >= stream_length) {
+ return false;
+ }
+
+ value = stream[decoder->stream_position++];
+ } else {
+ if (stream_length - decoder->stream_position < 2) {
+ return false;
+ }
+
+ uint8_t b0 = stream[decoder->stream_position++];
+ uint8_t b1 = stream[decoder->stream_position++];
+
+ decoder->match_distance = ((((uint32_t)b1 >> 4) << 8) | b0) + 1;
+ decoder->match_remaining = (b1 & 0x0f) + 3;
+
+ if (decoder->match_distance > decoder->filled) {
+ return false;
+ }
+
+ continue;
+ }
+ }
+
+ image->pixels[position++] ^= value;
+ decoder->window[decoder->window_position] = value;
+ decoder->window_position = (decoder->window_position + 1) & (TAIF_WINDOW_SIZE - 1);
+
+ if (decoder->filled < TAIF_WINDOW_SIZE) {
+ decoder->filled++;
+ }
+ }
+
+ bool eight_bpp = (image->flags & TAIF_FLAG_8BPP) != 0;
+ bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
+ uint32_t max_index = transparent ? image->palette_entries : (uint32_t)image->palette_entries - 1;
+ uint32_t full_bytes = eight_bpp ? image->width : image->width / 2;
+ bool odd_column = !eight_bpp && (image->width & 1) != 0;
+
+ for (uint32_t row = 0; row < image->height; row++) {
+ const uint8_t *line = image->pixels + (size_t)row * image->stride;
+
+ for (uint32_t i = 0; i < full_bytes; i++) {
+ if (eight_bpp) {
+ if (line[i] > max_index) {
+ return false;
+ }
+ } else if ((uint32_t)(line[i] >> 4) > max_index || (uint32_t)(line[i] & 0x0f) > max_index) {
+ return false;
+ }
+ }
+
+ if (odd_column && (uint32_t)(line[full_bytes] >> 4) > max_index) {
+ return false;
+ }
+ }
+
+ free(image->scaled);
+
+ image->scaled = 0;
+ image->frames_decoded++;
+
+ return true;
+}
+
+static ta_image_cache *find_image(const uint8_t *data, uint32_t length) {
+ for (ta_image_cache *image = image_cache; image != 0; image = image->next) {
+ if (image->data == data && image->length == length) {
+ return image;
+ }
+ }
+
+ return 0;
+}
+
+bool ta_image_init(const uint8_t *data, uint32_t length) {
+ if (find_image(data, length) != 0) {
+ return true;
+ }
+
+ ta_image_cache *image = (ta_image_cache *)malloc(sizeof(*image));
+ if (image == 0) {
+ return false;
+ }
+
+ *image = (ta_image_cache){0};
+
+ if (!parse_image(data, length, image)) {
+ free(image);
+ return false;
+ }
+
+ if (!decode_next_frame(image)) {
+ free(image->pixels);
+ free(image);
+ return false;
+ }
+
+ image->next = image_cache;
+ image_cache = image;
+ return true;
+}
+
+bool ta_image_get_size(const uint8_t *data, uint32_t length, uint32_t *width, uint32_t *height) {
+ ta_image_cache *image = find_image(data, length);
+ if (image == 0 || width == 0 || height == 0) {
+ return false;
+ }
+
+ *width = image->width;
+ *height = image->height;
+
+ return true;
+}
+
+static ta_image_cache *get_image(const uint8_t *data, uint32_t length, uint16_t frame) {
+ if (!ta_image_init(data, length)) {
+ return 0;
+ }
+
+ ta_image_cache *image = find_image(data, length);
+ if (frame >= image->frame_count) {
+ return 0;
+ }
+
+ uint32_t wanted = (uint32_t)frame + 1;
+
+ if (image->frames_decoded > wanted) {
+ decoder_reset(image);
+ }
+
+ while (image->frames_decoded < wanted) {
+ if (!decode_next_frame(image)) {
+ decoder_reset(image);
+ return 0;
+ }
+ }
+
+ return image;
+}
+
+static uint8_t get_pixel_index(const ta_image_cache *image, uint32_t x, uint32_t y) {
+ if ((image->flags & TAIF_FLAG_8BPP) != 0) {
+ return image->pixels[y * image->stride + x];
+ }
+
+ uint8_t packed = image->pixels[y * image->stride + x / 2];
+ return (x & 1) == 0 ? packed >> 4 : packed & 0x0f;
+}
+
+typedef struct {
+ uint16_t first;
+ uint16_t second;
+ uint8_t fraction;
+} ta_image_axis;
+
+static ta_image_axis axis_position(uint32_t index, uint32_t count, uint32_t source_count) {
+ ta_image_axis axis;
+
+#ifdef TA_DISABLE_BILINEAR_FILTERING
+ axis.first = (uint16_t)((uint64_t)index * source_count / count);
+ axis.second = axis.first;
+ axis.fraction = 0;
+#else
+ uint64_t position = ((uint64_t)(2 * (uint64_t)index + 1) * source_count * 128) / count;
+
+ if (position <= 128) {
+ axis.first = 0;
+ axis.second = 0;
+ axis.fraction = 0;
+ } else {
+ position -= 128;
+
+ uint32_t first = (uint32_t)(position >> 8);
+
+ if (first >= source_count - 1) {
+ axis.first = (uint16_t)(source_count - 1);
+ axis.second = axis.first;
+ axis.fraction = 0;
+ } else {
+ axis.first = (uint16_t)first;
+ axis.second = (uint16_t)(first + 1);
+ axis.fraction = (uint8_t)(position & 0xff);
+ }
+ }
+#endif
+
+ return axis;
+}
+
+static uint32_t sample_image(const ta_image_cache *image, ta_image_axis column, ta_image_axis row, uint32_t *red, uint32_t *green, uint32_t *blue) {
+ uint32_t weight_x0 = 256 - column.fraction;
+ uint32_t weight_y0 = 256 - row.fraction;
+ bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
+
+ uint32_t weights[4] = {
+ weight_x0 * weight_y0,
+ column.fraction * weight_y0,
+ weight_x0 * row.fraction,
+ column.fraction * row.fraction
+ };
+
+ uint8_t indices[4] = {
+ get_pixel_index(image, column.first, row.first),
+ get_pixel_index(image, column.second, row.first),
+ get_pixel_index(image, column.first, row.second),
+ get_pixel_index(image, column.second, row.second)
+ };
+
+ uint32_t red_sum = 0;
+ uint32_t green_sum = 0;
+ uint32_t blue_sum = 0;
+ uint32_t alpha_sum = 0;
+
+ for (uint32_t sample = 0; sample < 4; sample++) {
+ if (transparent && indices[sample] == 0) {
+ continue;
+ }
+
+ uint32_t palette_index = indices[sample] - (transparent ? 1 : 0);
+
+ red_sum += (uint32_t)image->palette[palette_index][0] * weights[sample];
+ green_sum += (uint32_t)image->palette[palette_index][1] * weights[sample];
+ blue_sum += (uint32_t)image->palette[palette_index][2] * weights[sample];
+ alpha_sum += weights[sample];
+ }
+
+ *red = red_sum;
+ *green = green_sum;
+ *blue = blue_sum;
+
+ return alpha_sum;
+}
+
+static void copy_bytes(uint8_t *destination, const uint8_t *source, size_t count) {
+ if ((((size_t)destination | (size_t)source) & 3) == 0) {
+ size_t words = count / 4;
+
+ for (size_t i = 0; i < words; i++) {
+ ((uint32_t *)destination)[i] = ((const uint32_t *)source)[i];
+ }
+
+ destination += words * 4;
+ source += words * 4;
+ count &= 3;
+ }
+
+ while (count--) {
+ *destination++ = *source++;
+ }
+}
+
+static bool prepare_scaled(ta_image_cache *image, uint32_t width, uint32_t height) {
+ if (image->scaled != 0 && image->scaled_width == width && image->scaled_height == height && image->scaled_bpp == ta_display->bpp) {
+ return true;
+ }
+
+ size_t bytes_per_pixel = ta_display->bpp / 8;
+ uint8_t *scaled = (uint8_t *)malloc((size_t)width * height * bytes_per_pixel);
+ ta_image_axis *columns = (ta_image_axis *)malloc((size_t)width * sizeof(*columns));
+
+ if (scaled == 0 || columns == 0) {
+ free(scaled);
+ free(columns);
+
+ return false;
+ }
+
+ for (uint32_t column = 0; column < width; column++) {
+ columns[column] = axis_position(column, width, image->width);
+ }
+
+ uint8_t *output = scaled;
+
+ for (uint32_t y = 0; y < height; y++) {
+ ta_image_axis row = axis_position(y, height, image->height);
+
+ for (uint32_t x = 0; x < width; x++) {
+ uint32_t red;
+ uint32_t green;
+ uint32_t blue;
+
+ sample_image(image, columns[x], row, &red, &green, &blue);
+
+ red = (red + 32768) >> 16;
+ green = (green + 32768) >> 16;
+ blue = (blue + 32768) >> 16;
+
+ if (ta_display->bpp == 32) {
+ *(uint32_t *)output = (red << 16) | (green << 8) | blue;
+ output += 4;
+ } else {
+ *output++ = (uint8_t)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
+ }
+ }
+ }
+
+ free(columns);
+ free(image->scaled);
+
+ image->scaled = scaled;
+ image->scaled_width = width;
+ image->scaled_height = height;
+ image->scaled_bpp = (uint8_t)ta_display->bpp;
+
+ return true;
+}
+
+bool ta_image_draw(const uint8_t *data, uint32_t length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, uint16_t frame) {
+ if (data == 0 || length == 0 || x > TA_FIXED_ONE || y > TA_FIXED_ONE || width == 0 || height == 0 || width > TA_FIXED_ONE || height > TA_FIXED_ONE || (!ta_display->sprite_mode && ta_display->buffers[1] == 0) || (ta_display->sprite_mode && (!ta_display_sprites || ta_display->bpp != 32)) || (ta_display->bpp != 8 && ta_display->bpp != 32)) {
+ return false;
+ }
+
+ uint32_t pixel_x = (uint32_t)(((uint64_t)x * ta_display->width) >> 16);
+ uint32_t pixel_y = (uint32_t)(((uint64_t)y * ta_display->height) >> 16);
+ uint32_t pixel_width = (uint32_t)(((uint64_t)width * ta_display->width) >> 16);
+ uint32_t pixel_height = (uint32_t)(((uint64_t)height * ta_display->height) >> 16);
+
+ if (pixel_width == 0 || pixel_height == 0) {
+ return false;
+ }
+
+ ta_image_cache *image = get_image(data, length, frame);
+ if (image == 0) {
+ return false;
+ }
+
+ uint32_t visible_width = 0;
+ uint32_t visible_height = 0;
+
+ if (pixel_x < ta_display->width) {
+ visible_width = pixel_width > ta_display->width - pixel_x ? (uint32_t)(ta_display->width - pixel_x) : pixel_width;
+ }
+
+ if (pixel_y < ta_display->height) {
+ visible_height = pixel_height > ta_display->height - pixel_y ? (uint32_t)(ta_display->height - pixel_y) : pixel_height;
+ }
+
+ if (visible_width == 0 || visible_height == 0) {
+ return true;
+ }
+
+ bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
+
+ if (ta_display->sprite_mode) {
+ uint32_t source_width = image->width;
+ uint32_t source_height = image->height;
+
+ if (visible_width < pixel_width) {
+ source_width = (uint32_t)(((uint64_t)visible_width * image->width + pixel_width - 1) / pixel_width);
+ }
+
+ if (visible_height < pixel_height) {
+ source_height = (uint32_t)(((uint64_t)visible_height * image->height + pixel_height - 1) / pixel_height);
+ }
+
+ ta_display_sprite *sprite = ta_display_sprite_create((uint16_t)source_width, (uint16_t)source_height);
+ if (!sprite) {
+ return false;
+ }
+
+ sprite->x = (uint16_t)pixel_x;
+ sprite->y = (uint16_t)pixel_y;
+ sprite->draw_width = (uint16_t)visible_width;
+ sprite->draw_height = (uint16_t)visible_height;
+
+ uint32_t *pixels = (uint32_t *)sprite->pixels;
+ uint32_t palette_offset = transparent ? 1 : 0;
+
+ for (uint32_t image_y = 0; image_y < source_height; image_y++) {
+ for (uint32_t image_x = 0; image_x < source_width; image_x++) {
+ uint8_t index = get_pixel_index(image, image_x, image_y);
+ uint32_t *pixel = &pixels[(size_t)image_y * source_width + image_x];
+
+ if (transparent && index == 0) {
+ *pixel = 0;
+ } else {
+ const uint8_t *color = image->palette[index - palette_offset];
+
+ *pixel = ((uint32_t)color[0] << 24) | ((uint32_t)color[1] << 16) | ((uint32_t)color[2] << 8) | 0xff;
+ }
+ }
+ }
+
+ if (!ta_display_sprite_add(sprite)) {
+ free(sprite);
+ return false;
+ }
+
+ return true;
+ }
+
+ if (!transparent && prepare_scaled(image, pixel_width, pixel_height)) {
+ size_t bytes_per_pixel = ta_display->bpp / 8;
+
+ for (uint32_t image_y = 0; image_y < visible_height; image_y++) {
+ const uint8_t *source = image->scaled + (size_t)image_y * pixel_width * bytes_per_pixel;
+ uint8_t *destination = (uint8_t *)ta_display->buffers[1] + ((size_t)(pixel_y + image_y) * ta_display->width + pixel_x) * bytes_per_pixel;
+
+ if (ta_display->bpp == 8) {
+ copy_bytes(destination, source, visible_width);
+ } else {
+ const uint32_t *source32 = (const uint32_t *)source;
+ uint32_t *destination32 = (uint32_t *)destination;
+
+ for (uint32_t image_x = 0; image_x < visible_width; image_x++) {
+ destination32[image_x] = (destination32[image_x] & 0xff000000u) | source32[image_x];
+ }
+ }
+ }
+
+ return true;
+ }
+
+ ta_image_axis *columns = (ta_image_axis *)malloc((size_t)visible_width * sizeof(*columns));
+ if (columns == 0) {
+ return false;
+ }
+
+ for (uint32_t image_x = 0; image_x < visible_width; image_x++) {
+ columns[image_x] = axis_position(image_x, pixel_width, image->width);
+ }
+
+ for (uint32_t image_y = 0; image_y < visible_height; image_y++) {
+ ta_image_axis row = axis_position(image_y, pixel_height, image->height);
+ size_t row_start = (size_t)(pixel_y + image_y) * ta_display->width + pixel_x;
+
+ for (uint32_t image_x = 0; image_x < visible_width; image_x++) {
+ uint32_t red;
+ uint32_t green;
+ uint32_t blue;
+ uint32_t alpha_sum = sample_image(image, columns[image_x], row, &red, &green, &blue);
+
+ if (alpha_sum == 0) {
+ continue;
+ }
+
+ size_t destination_index = row_start + image_x;
+ uint32_t inverse_alpha = 65536 - alpha_sum;
+
+ if (ta_display->bpp == 32) {
+ uint32_t *pixels = (uint32_t *)ta_display->buffers[1];
+ uint32_t destination = pixels[destination_index];
+
+ red = (red + ((destination >> 16) & 0xff) * inverse_alpha + 32768) >> 16;
+ green = (green + ((destination >> 8) & 0xff) * inverse_alpha + 32768) >> 16;
+ blue = (blue + (destination & 0xff) * inverse_alpha + 32768) >> 16;
+
+ pixels[destination_index] = (destination & 0xff000000u) | (red << 16) | (green << 8) | blue;
+ } else {
+ uint8_t *pixels = (uint8_t *)ta_display->buffers[1];
+ uint32_t destination = pixels[destination_index];
+
+ red = (red + (((destination >> 5) & 7) * 255 / 7) * inverse_alpha + 32768) >> 16;
+ green = (green + (((destination >> 2) & 7) * 255 / 7) * inverse_alpha + 32768) >> 16;
+ blue = (blue + ((destination & 3) * 255 / 3) * inverse_alpha + 32768) >> 16;
+
+ pixels[destination_index] = (uint8_t)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
+ }
+ }
+ }
+
+ free(columns);
+
+ return true;
+}
+
+bool ta_image_render(const uint8_t *data, uint32_t length, uint16_t frame, uint32_t width, uint32_t height, uint32_t *pixels) {
+ if (pixels == 0 || width == 0 || height == 0) {
+ return false;
+ }
+
+ ta_image_cache *image = get_image(data, length, frame);
+ if (image == 0) {
+ return false;
+ }
+
+ ta_image_axis *columns = (ta_image_axis *)malloc((size_t)width * sizeof(*columns));
+ if (columns == 0) {
+ return false;
+ }
+
+ for (uint32_t x = 0; x < width; x++) {
+ columns[x] = axis_position(x, width, image->width);
+ }
+
+ for (uint32_t y = 0; y < height; y++) {
+ ta_image_axis row = axis_position(y, height, image->height);
+
+ for (uint32_t x = 0; x < width; x++) {
+ uint32_t red;
+ uint32_t green;
+ uint32_t blue;
+
+ sample_image(image, columns[x], row, &red, &green, &blue);
+
+ red = (red + 32768) >> 16;
+ green = (green + 32768) >> 16;
+ blue = (blue + 32768) >> 16;
+
+ pixels[(size_t)y * width + x] = (red << 16) | (green << 8) | blue;
+ }
+ }
+
+ free(columns);
+
+ return true;
+}
+
+void ta_image_release(void) {
+ while (image_cache != 0) {
+ ta_image_cache *image = image_cache;
+ image_cache = image->next;
+ free(image->pixels);
+ free(image->scaled);
+ free(image);
+ }
+}
diff --git a/src/keyboard.c b/src/keyboard.c
@@ -0,0 +1,49 @@
+#include <stddef.h>
+#include <stdbool.h>
+#include "keyboard.h"
+
+ta_keyboards ta_keyboard;
+
+bool ta_keyboard_is_down(ta_keyboard_keys key) {
+ if (key == KEY_NONE) {
+ return false;
+ }
+
+ for (size_t i = 0; i < TA_KEYBOARD_MAX_KEYS_DOWN; i++) {
+ if (ta_keyboard.keys_down[i] == (uint8_t)key) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ta_keyboard_key_down(ta_keyboard_keys key) {
+ if (key == KEY_NONE || ta_keyboard_is_down(key)) {
+ return;
+ }
+
+ for (size_t i = 0; i < TA_KEYBOARD_MAX_KEYS_DOWN; i++) {
+ if (ta_keyboard.keys_down[i] == KEY_NONE) {
+ ta_keyboard.keys_down[i] = (uint8_t)key;
+
+ return;
+ }
+ }
+}
+
+void ta_keyboard_key_up(ta_keyboard_keys key) {
+ for (size_t i = 0; i < TA_KEYBOARD_MAX_KEYS_DOWN; i++) {
+ if (ta_keyboard.keys_down[i] == (uint8_t)key) {
+ ta_keyboard.keys_down[i] = KEY_NONE;
+
+ return;
+ }
+ }
+}
+
+void ta_keyboard_clear(void) {
+ for (size_t i = 0; i < TA_KEYBOARD_MAX_KEYS_DOWN; i++) {
+ ta_keyboard.keys_down[i] = KEY_NONE;
+ }
+}
diff --git a/src/main.c b/src/main.c
@@ -0,0 +1,43 @@
+#include "display.h"
+#include "fb.h"
+#include "api.h"
+#include "screen.h"
+#include "vm.h"
+
+void ta_main_destroy(void) {
+ int32_t exit_code = ta_vm_exit_code();
+
+ ta_screen_destroy();
+ ta_vm_unload();
+ ta_display_deinit();
+
+ ta_exit(exit_code);
+}
+
+bool ta_main_update(void) {
+ bool drawn = ta_screen_update();
+
+ if (!ta_vm_running) {
+ ta_main_destroy();
+ }
+
+ if (!drawn) {
+ return false;
+ }
+
+ ta_fb_swap(&ta_fb);
+
+ return true;
+}
+
+void ta_main_init(void) {
+ if (!ta_display_init() || (!ta_display->sprite_mode && !ta_fb_init(ta_display))) {
+ ta_exit(1);
+ }
+
+ ta_screen_init();
+
+ if (!ta_vm_load_file("game/tuxanci2.tab")) {
+ ta_exit(1);
+ }
+}
diff --git a/src/scene.c b/src/scene.c
@@ -0,0 +1,206 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include "fixed.h"
+#include "fb.h"
+#include "image.h"
+#include "text.h"
+#include "shapes.h"
+
+typedef struct {
+ const uint8_t *data;
+ ta_text *text;
+ uint32_t length;
+ uint32_t x;
+ uint32_t y;
+ uint32_t width;
+ uint32_t height;
+ uint16_t frame;
+ uint32_t shape_type;
+ uint32_t shape_args[6];
+ uint32_t color;
+ uint8_t sprites_after;
+} ta_scene_op;
+
+static ta_scene_op *ops[2];
+static size_t counts[2];
+static size_t capacities[2];
+static uint8_t current;
+static bool valid;
+static uint16_t last_width;
+static uint16_t last_height;
+static uint8_t last_bpp;
+
+static bool same(const ta_scene_op *a, const ta_scene_op *b) {
+ return a->data == b->data
+ && a->text == b->text
+ && a->length == b->length
+ && a->x == b->x
+ && a->y == b->y
+ && a->width == b->width
+ && a->height == b->height
+ && a->frame == b->frame
+ && a->shape_type == b->shape_type
+ && a->color == b->color
+ && a->shape_args[0] == b->shape_args[0]
+ && a->shape_args[1] == b->shape_args[1]
+ && a->shape_args[2] == b->shape_args[2]
+ && a->shape_args[3] == b->shape_args[3]
+ && a->shape_args[4] == b->shape_args[4]
+ && a->shape_args[5] == b->shape_args[5];
+}
+
+static ta_scene_op *push(void) {
+ if (counts[current] == capacities[current]) {
+ size_t new_capacity = capacities[current] + 1;
+ ta_scene_op *grown = (ta_scene_op *)malloc(new_capacity * sizeof(ta_scene_op));
+
+ if (grown == NULL) {
+ return NULL;
+ }
+
+ for (size_t i = 0; i < counts[current]; i++) {
+ grown[i] = ops[current][i];
+ }
+
+ free(ops[current]);
+
+ ops[current] = grown;
+ capacities[current] = new_capacity;
+ }
+
+ ta_scene_op *op = &ops[current][counts[current]++];
+
+ op->data = NULL;
+ op->text = NULL;
+ op->length = 0;
+ op->x = 0;
+ op->y = 0;
+ op->width = 0;
+ op->height = 0;
+ op->frame = 0;
+ op->shape_type = 0;
+ for (uint32_t i = 0; i < 6; i++) op->shape_args[i] = 0;
+ op->color = 0;
+ op->sprites_after = 0;
+
+ return op;
+}
+
+static void sprites_trim(uint8_t count) {
+ while (ta_display_sprites && ta_display_sprites->count > count) {
+ ta_display_sprite_free(ta_display_sprites->sprites[ta_display_sprites->count - 1]);
+ }
+}
+
+void ta_scene_invalidate(void) {
+ valid = false;
+}
+
+void ta_scene_begin(void) {
+ counts[current] = 0;
+
+ if (ta_display->width != last_width || ta_display->height != last_height || ta_display->bpp != last_bpp) {
+ last_width = ta_display->width;
+ last_height = ta_display->height;
+ last_bpp = ta_display->bpp;
+ valid = false;
+ }
+}
+
+bool ta_scene_image(const uint8_t *data, uint32_t length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, uint16_t frame) {
+ ta_scene_op *op = push();
+
+ if (op == NULL) {
+ return false;
+ }
+
+ op->data = data;
+ op->length = length;
+ op->x = x;
+ op->y = y;
+ op->width = width;
+ op->height = height;
+ op->frame = frame;
+
+ return true;
+}
+
+bool ta_scene_shape(uint32_t type, const uint32_t *args, uint32_t color) {
+ ta_scene_op *op = push();
+ if (op == NULL || args == NULL || type < 1 || type > 4) return false;
+ op->shape_type = type;
+ op->color = color;
+ for (uint32_t i = 0; i < 6; i++) op->shape_args[i] = args[i];
+ return true;
+}
+
+bool ta_scene_text(ta_text *text, uint32_t x, uint32_t y) {
+ ta_scene_op *op = push();
+
+ if (op == NULL) {
+ return false;
+ }
+
+ op->text = text;
+ op->x = x;
+ op->y = y;
+
+ return true;
+}
+
+bool ta_scene_end(void) {
+ ta_scene_op *now = ops[current];
+ ta_scene_op *before = ops[current ^ 1];
+ size_t now_count = counts[current];
+ size_t before_count = counts[current ^ 1];
+ size_t prefix = 0;
+
+ if (valid) {
+ while (prefix < now_count && prefix < before_count && same(&now[prefix], &before[prefix])) {
+ prefix++;
+ }
+
+ if (prefix == now_count && prefix == before_count) {
+ return false;
+ }
+ }
+
+ size_t start = ta_display->sprite_mode ? prefix : 0;
+
+ if (start == 0) {
+ ta_fb_clear(ta_display);
+ } else {
+ sprites_trim(before[start - 1].sprites_after);
+
+ for (size_t i = 0; i < start; i++) {
+ now[i].sprites_after = before[i].sprites_after;
+ }
+ }
+
+ for (size_t i = start; i < now_count; i++) {
+ ta_scene_op *op = &now[i];
+
+ if (op->shape_type != 0) {
+ uint32_t *a = op->shape_args;
+ if (op->shape_type == 1) ta_shapes_draw_pixel(a[0], a[1], op->color);
+ else if (op->shape_type == 2) ta_shapes_draw_rectangle(a[0], a[1], a[2], a[3], op->color);
+ else if (op->shape_type == 3) ta_shapes_draw_triangle(a[0], a[1], a[2], a[3], a[4], a[5], op->color);
+ else ta_shapes_draw_oval(a[0], a[1], a[2], a[3], op->color);
+ } else if (op->text != NULL) {
+ op->text->x = op->x;
+ op->text->y = op->y;
+ ta_text_draw(op->text);
+ } else {
+ ta_image_draw(op->data, op->length, op->x, op->y, op->width, op->height, op->frame);
+ }
+
+ op->sprites_after = ta_display_sprites ? ta_display_sprites->count : 0;
+ }
+
+ valid = true;
+ current ^= 1;
+
+ return true;
+}
diff --git a/src/screen.c b/src/screen.c
@@ -0,0 +1,136 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "vm.h"
+#include "scene.h"
+#include "image.h"
+
+#define TA_SCREEN_NONE (-1)
+
+typedef struct {
+ uint32_t init;
+ uint32_t update;
+ uint32_t destroy;
+ bool registered;
+} ta_screen_entry;
+
+static ta_screen_entry *screens;
+static uint32_t screen_count;
+static int32_t current_screen;
+static int32_t pending_screen;
+
+static void screens_clear(void) {
+ if (screens != NULL) {
+ free(screens);
+ }
+
+ screens = NULL;
+ screen_count = 0;
+}
+
+static bool screens_grow(uint32_t new_count) {
+ ta_screen_entry *grown = (ta_screen_entry *)malloc((size_t)new_count * sizeof(ta_screen_entry));
+
+ if (grown == NULL) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < screen_count; i++) {
+ grown[i] = screens[i];
+ }
+
+ for (uint32_t i = screen_count; i < new_count; i++) {
+ grown[i].init = 0;
+ grown[i].update = 0;
+ grown[i].destroy = 0;
+ grown[i].registered = false;
+ }
+
+ if (screens != NULL) {
+ free(screens);
+ }
+
+ screens = grown;
+ screen_count = new_count;
+
+ return true;
+}
+
+void ta_screen_init(void) {
+ screens_clear();
+
+ current_screen = TA_SCREEN_NONE;
+ pending_screen = TA_SCREEN_NONE;
+}
+
+bool ta_screen_register(uint32_t id, uint32_t init, uint32_t update, uint32_t destroy) {
+ if (id == 0xFFFFFFFFu) {
+ return false;
+ }
+
+ if (id >= screen_count && !screens_grow(id + 1)) {
+ return false;
+ }
+
+ screens[id].init = init;
+ screens[id].update = update;
+ screens[id].destroy = destroy;
+ screens[id].registered = true;
+
+ return true;
+}
+
+bool ta_screen_switch(uint32_t id) {
+ if (id >= screen_count || !screens[id].registered) {
+ return false;
+ }
+
+ pending_screen = (int32_t)id;
+
+ return true;
+}
+
+bool ta_screen_update(void) {
+ while (pending_screen != TA_SCREEN_NONE) {
+ int32_t next_screen = pending_screen;
+
+ pending_screen = TA_SCREEN_NONE;
+
+ if (current_screen != TA_SCREEN_NONE && screens[current_screen].destroy != 0) {
+ if (!ta_vm_call(screens[current_screen].destroy, 0, 0, 0, NULL)) {
+ return false;
+ }
+ }
+
+ current_screen = next_screen;
+
+ if (screens[current_screen].init != 0) {
+ if (!ta_vm_call(screens[current_screen].init, 0, 0, 0, NULL)) {
+ return false;
+ }
+ }
+ }
+
+ if (current_screen == TA_SCREEN_NONE || screens[current_screen].update == 0) {
+ return false;
+ }
+
+ ta_scene_begin();
+
+ if (!ta_vm_call(screens[current_screen].update, 0, 0, 0, NULL)) {
+ return false;
+ }
+
+ return ta_scene_end();
+}
+
+void ta_screen_destroy(void) {
+ current_screen = TA_SCREEN_NONE;
+ pending_screen = TA_SCREEN_NONE;
+ ta_scene_invalidate();
+
+ screens_clear();
+
+ ta_image_release();
+}
diff --git a/src/shapes.c b/src/shapes.c
@@ -0,0 +1,122 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "display.h"
+
+typedef struct {
+ uint32_t x;
+ uint32_t y;
+ uint32_t width;
+ uint32_t height;
+ ta_display_sprite *sprite;
+} ta_shape_target;
+
+static ta_shape_target target;
+
+static bool target_begin(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
+ if (width == 0 || height == 0 || x >= ta_display->width || y >= ta_display->height) return false;
+ if (width > ta_display->width - x) width = ta_display->width - x;
+ if (height > ta_display->height - y) height = ta_display->height - y;
+ target.x = x;
+ target.y = y;
+ target.width = width;
+ target.height = height;
+ target.sprite = NULL;
+ if (ta_display->sprite_mode) {
+ if (ta_display->bpp != 32) return false;
+ target.sprite = ta_display_sprite_create((uint16_t)width, (uint16_t)height);
+ if (!target.sprite) return false;
+ target.sprite->x = (uint16_t)x;
+ target.sprite->y = (uint16_t)y;
+ for (size_t i = 0; i < (size_t)width * height * 4; i++) target.sprite->pixels[i] = 0;
+ } else if (!ta_display->buffers[1] || (ta_display->bpp != 8 && ta_display->bpp != 32)) {
+ return false;
+ }
+ return true;
+}
+
+static void target_pixel(uint32_t x, uint32_t y, uint32_t color) {
+ if (x < target.x || y < target.y || x - target.x >= target.width || y - target.y >= target.height) return;
+ size_t local = (size_t)(y - target.y) * target.width + (x - target.x);
+ if (target.sprite) {
+ ((uint32_t *)target.sprite->pixels)[local] = ((color & 0xffffffu) << 8) | 0xffu;
+ } else {
+ size_t index = (size_t)y * ta_display->width + x;
+ if (ta_display->bpp == 32) {
+ ((uint32_t *)ta_display->buffers[1])[index] = color & 0xffffffu;
+ } else {
+ uint32_t red = (color >> 16) & 255;
+ uint32_t green = (color >> 8) & 255;
+ uint32_t blue = color & 255;
+ ((uint8_t *)ta_display->buffers[1])[index] = (uint8_t)((red & 0xe0) | ((green >> 3) & 0x1c) | (blue >> 6));
+ }
+ }
+}
+
+static void target_end(void) {
+ if (target.sprite && !ta_display_sprite_add(target.sprite)) free(target.sprite);
+ target.sprite = NULL;
+}
+
+void ta_shapes_draw_pixel(uint32_t x, uint32_t y, uint32_t color) {
+ if (!target_begin(x, y, 1, 1)) return;
+ target_pixel(x, y, color);
+ target_end();
+}
+
+void ta_shapes_draw_rectangle(uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t color) {
+ if (!target_begin(x, y, width, height)) return;
+ uint32_t end_x = target.x + target.width;
+ uint32_t end_y = target.y + target.height;
+ for (uint32_t py = target.y; py < end_y; py++)
+ for (uint32_t px = target.x; px < end_x; px++) target_pixel(px, py, color);
+ target_end();
+}
+
+static int64_t edge(int32_t ax, int32_t ay, int32_t bx, int32_t by, int32_t px, int32_t py) {
+ return ((int64_t)px - ax) * ((int64_t)by - ay) - ((int64_t)py - ay) * ((int64_t)bx - ax);
+}
+
+void ta_shapes_draw_triangle(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2, uint32_t x3, uint32_t y3, uint32_t color) {
+ uint32_t min_x = x1, max_x = x1, min_y = y1, max_y = y1;
+ if (x2 < min_x) min_x = x2;
+ if (x3 < min_x) min_x = x3;
+ if (x2 > max_x) max_x = x2;
+ if (x3 > max_x) max_x = x3;
+ if (y2 < min_y) min_y = y2;
+ if (y3 < min_y) min_y = y3;
+ if (y2 > max_y) max_y = y2;
+ if (y3 > max_y) max_y = y3;
+ if (max_x == 0xffffffffu || max_y == 0xffffffffu) return;
+ uint32_t width = max_x - min_x + 1, height = max_y - min_y + 1;
+ if (!target_begin(min_x, min_y, width, height)) return;
+ int64_t area = edge((int32_t)x1, (int32_t)y1, (int32_t)x2, (int32_t)y2, (int32_t)x3, (int32_t)y3);
+ if (area != 0) {
+ uint32_t end_x = target.x + target.width, end_y = target.y + target.height;
+ for (uint32_t y = target.y; y < end_y; y++) for (uint32_t x = target.x; x < end_x; x++) {
+ int64_t a = edge((int32_t)x1, (int32_t)y1, (int32_t)x2, (int32_t)y2, (int32_t)x, (int32_t)y);
+ int64_t b = edge((int32_t)x2, (int32_t)y2, (int32_t)x3, (int32_t)y3, (int32_t)x, (int32_t)y);
+ int64_t c = edge((int32_t)x3, (int32_t)y3, (int32_t)x1, (int32_t)y1, (int32_t)x, (int32_t)y);
+ if ((area > 0 && a >= 0 && b >= 0 && c >= 0) || (area < 0 && a <= 0 && b <= 0 && c <= 0)) target_pixel(x, y, color);
+ }
+ }
+ target_end();
+}
+
+void ta_shapes_draw_oval(uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t color) {
+ if (!target_begin(x, y, width, height)) return;
+ int64_t rx = (int64_t)width / 2;
+ int64_t ry = (int64_t)height / 2;
+ int64_t cx = (int64_t)x + rx;
+ int64_t cy = (int64_t)y + ry;
+ int64_t rx2 = rx * rx, ry2 = ry * ry;
+ if (rx2 == 0) rx2 = 1;
+ if (ry2 == 0) ry2 = 1;
+ uint32_t end_x = target.x + target.width, end_y = target.y + target.height;
+ for (uint32_t py = target.y; py < end_y; py++) for (uint32_t px = target.x; px < end_x; px++) {
+ int64_t dx = (int64_t)px - cx, dy = (int64_t)py - cy;
+ if (dx * dx * ry2 + dy * dy * rx2 <= rx2 * ry2) target_pixel(px, py, color);
+ }
+ target_end();
+}
diff --git a/src/syscall.c b/src/syscall.c
@@ -0,0 +1,296 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "vm.h"
+#include "text.h"
+#include "abi.h"
+#include "scene.h"
+#include "screen.h"
+#include "keyboard.h"
+#include "image.h"
+
+static bool image_data(const ta_vm *vm, uint32_t address, const uint8_t **data, uint32_t *length) {
+ *data = (const uint8_t *)ta_vm_translate_pointer(vm, address, 1);
+
+ if (*data == NULL) {
+ return false;
+ }
+
+ *length = vm->mem_size - address;
+
+ return true;
+}
+
+static ta_text **texts;
+static uint32_t text_count;
+
+static ta_text *text_get(uint32_t handle) {
+ if (handle == 0 || handle > text_count) {
+ return NULL;
+ }
+
+ return texts[handle - 1];
+}
+
+static bool text_slot_find(uint32_t *slot) {
+ for (uint32_t i = 0; i < text_count; i++) {
+ if (texts[i] == NULL) {
+ *slot = i;
+
+ return true;
+ }
+ }
+
+ ta_text **grown = (ta_text **)malloc((size_t)(text_count + 1) * sizeof(ta_text *));
+
+ if (grown == NULL) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < text_count; i++) {
+ grown[i] = texts[i];
+ }
+
+ grown[text_count] = NULL;
+
+ if (texts != NULL) {
+ free(texts);
+ }
+
+ texts = grown;
+ *slot = text_count;
+ text_count++;
+
+ return true;
+}
+
+static uint32_t text_create(const char *string, uint32_t size, uint32_t color, uint32_t border_width, uint32_t border_color) {
+ if (size == 0) {
+ return 0;
+ }
+
+ ta_text *text = (ta_text *)malloc(sizeof(ta_text));
+
+ if (text == NULL) {
+ return 0;
+ }
+
+ text->text = string;
+ text->x = 0;
+ text->y = 0;
+ text->size = (uint8_t)size;
+ text->red = (uint8_t)((color >> 16) & 255);
+ text->green = (uint8_t)((color >> 8) & 255);
+ text->blue = (uint8_t)(color & 255);
+ text->border_width = (uint8_t)border_width;
+ text->border_red = (uint8_t)((border_color >> 16) & 255);
+ text->border_green = (uint8_t)((border_color >> 8) & 255);
+ text->border_blue = (uint8_t)(border_color & 255);
+ text->width = 0;
+ text->height = 0;
+ text->sprite = NULL;
+
+ ta_text_rasterize(text);
+
+ text->text = NULL;
+
+ if (!text->sprite) {
+ free(text);
+
+ return 0;
+ }
+
+ uint32_t slot;
+
+ if (!text_slot_find(&slot)) {
+ ta_text_destroy(text);
+ free(text);
+
+ return 0;
+ }
+
+ texts[slot] = text;
+
+ return slot + 1;
+}
+
+static void text_destroy(uint32_t handle) {
+ ta_text *text = text_get(handle);
+
+ if (!text) {
+ return;
+ }
+
+ ta_scene_invalidate();
+ ta_text_destroy(text);
+ free(text);
+ texts[handle - 1] = NULL;
+}
+
+ta_vm_status ta_syscall(ta_vm *vm) {
+ uint32_t *registers = vm->regs;
+ uint32_t *args = ®isters[TA_VM_REG_A0];
+ uint32_t result = 0;
+
+ switch (registers[TA_VM_REG_A7]) {
+ case TA_SYS_DISPLAY_WIDTH: {
+ result = ta_display->width;
+
+ break;
+ }
+ case TA_SYS_DISPLAY_HEIGHT: {
+ result = ta_display->height;
+
+ break;
+ }
+ case TA_SYS_DISPLAY_SPRITE_MODE: {
+ result = ta_display->sprite_mode ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_KEYBOARD_IS_DOWN: {
+ result = args[0] < 256 && ta_keyboard_is_down((ta_keyboard_keys)args[0]) ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_IMAGE_INIT: {
+ const uint8_t *data;
+ uint32_t length;
+
+ if (!image_data(vm, args[0], &data, &length)) {
+ return TA_VM_FAULT_MEMORY;
+ }
+
+ result = ta_image_init(data, length) ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_IMAGE_DRAW: {
+ const uint8_t *data;
+ uint32_t length;
+
+ if (!image_data(vm, args[0], &data, &length)) {
+ return TA_VM_FAULT_MEMORY;
+ }
+
+ result = ta_scene_image(data, length, args[1], args[2], args[3], args[4], (uint16_t)args[5]) ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_IMAGE_GET_SIZE: {
+ const uint8_t *data;
+ uint32_t length;
+ uint32_t width;
+ uint32_t height;
+
+ if (!image_data(vm, args[0], &data, &length)) {
+ return TA_VM_FAULT_MEMORY;
+ }
+
+ if (!ta_image_get_size(data, length, &width, &height)) {
+ break;
+ }
+
+ if (!ta_vm_write32(vm, args[1], width) || !ta_vm_write32(vm, args[2], height)) {
+ return TA_VM_FAULT_MEMORY;
+ }
+
+ result = 1;
+
+ break;
+ }
+ case TA_SYS_TEXT_CREATE: {
+ const char *string = ta_vm_cstr(vm, args[0]);
+
+ if (!string) {
+ return TA_VM_FAULT_MEMORY;
+ }
+
+ result = text_create(string, args[1], args[2], args[3], args[4]);
+
+ break;
+ }
+ case TA_SYS_TEXT_WIDTH: {
+ ta_text *text = text_get(args[0]);
+
+ result = text ? text->width : 0;
+
+ break;
+ }
+ case TA_SYS_TEXT_HEIGHT: {
+ ta_text *text = text_get(args[0]);
+
+ result = text ? text->height : 0;
+
+ break;
+ }
+ case TA_SYS_TEXT_DRAW: {
+ ta_text *text = text_get(args[0]);
+
+ if (text) {
+ ta_scene_text(text, args[1], args[2]);
+ }
+
+ break;
+ }
+ case TA_SYS_TEXT_DESTROY: {
+ text_destroy(args[0]);
+
+ break;
+ }
+ case TA_SYS_SCREEN_REGISTER: {
+ result = ta_screen_register(args[0], args[1], args[2], args[3]) ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_SCREEN_SWITCH: {
+ result = ta_screen_switch(args[0]) ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_SHAPES_PIXEL: {
+ uint32_t shape_args[6] = {args[0], args[1], 0, 0, 0, 0};
+ result = ta_scene_shape(1, shape_args, args[2]) ? 1 : 0;
+ break;
+ }
+ case TA_SYS_SHAPES_RECTANGLE:
+ case TA_SYS_SHAPES_OVAL: {
+ uint32_t shape_args[6] = {args[0], args[1], args[2], args[3], 0, 0};
+ result = ta_scene_shape(registers[TA_VM_REG_A7] == TA_SYS_SHAPES_RECTANGLE ? 2 : 4, shape_args, args[4]) ? 1 : 0;
+ break;
+ }
+ case TA_SYS_SHAPES_TRIANGLE: {
+ const uint32_t *coordinates = (const uint32_t *)ta_vm_translate_pointer(vm, args[0], 6 * sizeof(uint32_t));
+ if (coordinates == NULL) return TA_VM_FAULT_MEMORY;
+ result = ta_scene_shape(3, coordinates, args[1]) ? 1 : 0;
+ break;
+ }
+ default: {
+ return TA_VM_FAULT_ILLEGAL;
+ }
+ }
+
+ registers[TA_VM_REG_A0] = result;
+
+ return TA_VM_RUNNING;
+}
+
+void ta_syscall_reset(void) {
+ ta_scene_invalidate();
+ ta_image_release();
+
+ for (uint32_t i = 0; i < text_count; i++) {
+ if (texts[i] != NULL) {
+ ta_text_destroy(texts[i]);
+ free(texts[i]);
+ }
+ }
+
+ if (texts != NULL) {
+ free(texts);
+ }
+
+ texts = NULL;
+ text_count = 0;
+}
diff --git a/src/ta_display.c b/src/ta_display.c
@@ -1,135 +0,0 @@
-#include "ta_display.h"
-#include "ta_api.h"
-
-static ta_display_t default_display;
-ta_display_t *ta_display = &default_display;
-ta_display_sprites_t *ta_display_sprites;
-
-ta_bool ta_display_init(void) {
- if (ta_display_sprites) {
- return ta_true;
- }
-
- ta_display_sprites = ta_malloc(sizeof(*ta_display_sprites));
- if (!ta_display_sprites) {
- return ta_false;
- }
-
- ta_display_sprites->count = 0;
- ta_display_sprites->capacity = 0;
- ta_display_sprites->sprites = TA_NULL;
-
- return ta_true;
-}
-
-void ta_display_deinit(void) {
- if (ta_display_sprites) {
- for (ta_uint8 i = 0; i < ta_display_sprites->count; i++) {
- ta_free(ta_display_sprites->sprites[i]);
- }
-
- ta_free(ta_display_sprites->sprites);
- ta_free(ta_display_sprites);
- ta_display_sprites = TA_NULL;
- }
-
- ta_free(ta_display->buffers[0]);
- ta_free(ta_display->buffers[1]);
- ta_display->buffers[0] = TA_NULL;
- ta_display->buffers[1] = TA_NULL;
- ta_display->size = 0;
-}
-
-ta_display_sprite *ta_display_sprite_create(ta_uint16 width, ta_uint16 height) {
- if (width == 0 || height == 0) {
- return TA_NULL;
- }
-
- ta_size max_size = (ta_size)-1;
- ta_size pixel_count = (ta_size)width * height;
-
- if (pixel_count > (max_size - sizeof(ta_display_sprite)) / sizeof(ta_uint32)) {
- return TA_NULL;
- }
-
- ta_display_sprite *sprite = ta_malloc(sizeof(*sprite) + pixel_count * sizeof(ta_uint32));
- if (!sprite) {
- return TA_NULL;
- }
-
- sprite->x = 0;
- sprite->y = 0;
- sprite->width = width;
- sprite->height = height;
- sprite->draw_width = width;
- sprite->draw_height = height;
- sprite->format = TA_DISPLAY_PIXEL_FORMAT_32;
-
- return sprite;
-}
-
-ta_bool ta_display_sprite_add(ta_display_sprite *sprite) {
- if (!sprite || !ta_display_sprites) {
- return ta_false;
- }
-
- ta_display_sprites_t *list = ta_display_sprites;
-
- if (list->count == list->capacity) {
- if (list->capacity == 255) {
- return ta_false;
- }
-
- ta_uint16 new_capacity = list->capacity ? (ta_uint16)list->capacity * 2 : 8;
- if (new_capacity > 255) {
- new_capacity = 255;
- }
-
- ta_display_sprite **new_sprites = ta_malloc(new_capacity * sizeof(ta_display_sprite *));
- if (!new_sprites) {
- return ta_false;
- }
-
- for (ta_uint8 i = 0; i < list->count; i++) {
- new_sprites[i] = list->sprites[i];
- }
-
- ta_free(list->sprites);
- list->sprites = new_sprites;
- list->capacity = (ta_uint8)new_capacity;
- }
-
- list->sprites[list->count++] = sprite;
- return ta_true;
-}
-
-void ta_display_sprite_free(ta_display_sprite *sprite) {
- if (!sprite) {
- return;
- }
-
- if (ta_display_sprites) {
- for (ta_uint8 i = 0; i < ta_display_sprites->count; i++) {
- if (ta_display_sprites->sprites[i] == sprite) {
- for (ta_uint8 j = i; j < ta_display_sprites->count - 1; j++) {
- ta_display_sprites->sprites[j] = ta_display_sprites->sprites[j + 1];
- }
-
- ta_display_sprites->count--;
- break;
- }
- }
- }
-
- ta_free(sprite);
-}
-
-void ta_display_sprites_clear(void) {
- if (!ta_display_sprites) {
- return;
- }
-
- while (ta_display_sprites->count != 0) {
- ta_display_sprite_free(ta_display_sprites->sprites[ta_display_sprites->count - 1]);
- }
-}
diff --git a/src/ta_fb.c b/src/ta_fb.c
@@ -1,80 +0,0 @@
-#include "ta_api.h"
-
-ta_size ta_fb_init(ta_fbs *fb) {
- if (fb == 0 || fb->width == 0 || fb->height == 0 || fb->bpp == 0 || fb->bpp % 8 != 0) {
- return 0;
- }
-
- ta_size bytes_per_pixel = fb->bpp / 8;
- ta_size max_size = (ta_size)-1;
- if (fb->width > max_size / bytes_per_pixel || fb->height > max_size / (fb->width * bytes_per_pixel)) {
- return 0;
- }
-
- ta_size size = fb->width * fb->height * bytes_per_pixel;
- ta_free(fb->buffers[0]);
- ta_free(fb->buffers[1]);
- fb->buffers[0] = 0;
- fb->buffers[1] = 0;
-
- void *first = ta_malloc(size);
- if (first == 0) {
- return 0;
- }
-
- void *second = ta_malloc(size);
- if (second == 0) {
- ta_free(first);
- return 0;
- }
-
- fb->size = size;
- fb->buffers[0] = first;
- fb->buffers[1] = second;
- if (fb->bpp == 8) {
- fb->format = TA_DISPLAY_PIXEL_FORMAT_8;
- } else if (fb->bpp == 16) {
- fb->format = TA_DISPLAY_PIXEL_FORMAT_16;
- } else {
- fb->format = TA_DISPLAY_PIXEL_FORMAT_32;
- }
- return size;
-}
-
-void ta_fb_swap(ta_fbs *fb) {
- if (!fb || !fb->buffers[0] || !fb->buffers[1]) {
- return;
- }
-
- void *temp = fb->buffers[0];
- fb->buffers[0] = fb->buffers[1];
- fb->buffers[1] = temp;
-}
-
-void ta_fb_clear(ta_fbs *fb) {
- if (!fb) {
- return;
- }
-
- if (fb->sprite_mode) {
- ta_display_sprites_clear();
- return;
- }
-
- if (!fb->buffers[1] || fb->size == 0) {
- return;
- }
-
- ta_size *dest = (ta_size *)fb->buffers[1];
- ta_size num_words = fb->size / sizeof(ta_size);
-
- for (ta_size i = 0; i < num_words; i++) {
- dest[i] = 0;
- }
-
- unsigned char *byte_dest = (unsigned char *)fb->buffers[1];
- ta_size handled_bytes = num_words * sizeof(ta_size);
- for (ta_size i = handled_bytes; i < fb->size; i++) {
- byte_dest[i] = 0;
- }
-}
diff --git a/src/ta_image.c b/src/ta_image.c
@@ -1,694 +0,0 @@
-#include "ta_api.h"
-
-#define TAIF_FLAG_8BPP 0x01
-#define TAIF_FLAG_TRANSPARENT 0x02
-#define TAIF_FLAG_ANIMATED 0x04
-#define TAIF_WINDOW_SIZE 4096
-
-typedef struct {
- ta_uint8 window[TAIF_WINDOW_SIZE];
- ta_uint32 stream_position;
- ta_uint32 window_position;
- ta_uint32 filled;
- ta_uint32 match_distance;
- ta_uint32 match_remaining;
- ta_uint8 item_flags;
- ta_uint8 flags_remaining;
-} ta_image_decoder;
-
-typedef struct ta_image_cache {
- const ta_uint8 *data;
- ta_uint32 length;
- ta_uint32 width;
- ta_uint32 height;
- ta_uint32 stride;
- ta_uint32 frame_size;
- ta_uint16 frame_count;
- ta_uint16 palette_entries;
- ta_uint32 frames_decoded;
- ta_uint8 flags;
- ta_uint8 palette[256][3];
- const ta_uint8 *stream;
- ta_uint32 stream_length;
- ta_image_decoder decoder;
- ta_uint8 *pixels;
- ta_uint8 *scaled;
- ta_uint32 scaled_width;
- ta_uint32 scaled_height;
- ta_uint8 scaled_bpp;
- struct ta_image_cache *next;
-} ta_image_cache;
-
-static ta_image_cache *image_cache;
-
-static ta_uint16 read_le16(const ta_uint8 *data) {
- return (ta_uint16)((ta_uint16)data[0] | ((ta_uint16)data[1] << 8));
-}
-
-static ta_bool parse_image(const ta_uint8 *data, ta_uint32 length, ta_image_cache *image) {
- if (data == 0 || length < 11 || data[0] != 'T' || data[1] != 'A' || data[2] != 'I' || data[3] != 'F' || data[4] != 1) {
- return ta_false;
- }
-
- ta_uint8 flags = data[5];
- if ((flags & 0xf8) != 0) {
- return ta_false;
- }
-
- ta_uint32 width = read_le16(data + 6);
- ta_uint32 height = read_le16(data + 8);
- ta_uint32 palette_entries = (ta_uint32)data[10] + 1;
- ta_bool eight_bpp = (flags & TAIF_FLAG_8BPP) != 0;
- ta_bool transparent = (flags & TAIF_FLAG_TRANSPARENT) != 0;
- ta_uint32 max_entries = eight_bpp ? (transparent ? 255 : 256) : (transparent ? 15 : 16);
- ta_uint32 header_size = 11;
- ta_uint16 frame_count = 1;
-
- if (width == 0 || height == 0 || palette_entries > max_entries) {
- return ta_false;
- }
-
- if ((flags & TAIF_FLAG_ANIMATED) != 0) {
- if (length < 14 || data[11] == 0) {
- return ta_false;
- }
-
- frame_count = read_le16(data + 12);
- if (frame_count < 2) {
- return ta_false;
- }
-
- header_size = 14;
- }
-
- ta_uint64 stride = eight_bpp ? width : (width + 1) / 2;
- ta_uint64 frame_size = stride * height;
- ta_uint64 max_size = (ta_uint64)(ta_size)-1;
- ta_uint64 palette_size = (ta_uint64)palette_entries * 3;
-
- if (frame_size == 0 || frame_size > max_size || frame_size > 0xffffffffu || header_size + palette_size > length) {
- return ta_false;
- }
-
- const ta_uint8 *palette = data + header_size;
-
- image->pixels = (ta_uint8 *)ta_malloc((ta_size)frame_size);
- if (image->pixels == 0) {
- return ta_false;
- }
-
- for (ta_uint32 i = 0; i < (ta_uint32)frame_size; i++) {
- image->pixels[i] = 0;
- }
-
- image->data = data;
- image->length = length;
- image->width = width;
- image->height = height;
- image->stride = (ta_uint32)stride;
- image->frame_size = (ta_uint32)frame_size;
- image->frame_count = frame_count;
- image->palette_entries = (ta_uint16)palette_entries;
- image->flags = flags;
- image->stream = palette + palette_size;
- image->stream_length = length - (ta_uint32)(header_size + palette_size);
-
- for (ta_uint32 i = 0; i < palette_entries; i++) {
- for (ta_uint32 channel = 0; channel < 3; channel++) {
- image->palette[i][channel] = palette[i * 3 + channel];
- }
- }
-
- return ta_true;
-}
-
-static void decoder_reset(ta_image_cache *image) {
- ta_image_decoder *decoder = &image->decoder;
-
- decoder->stream_position = 0;
- decoder->window_position = 0;
- decoder->filled = 0;
- decoder->match_distance = 0;
- decoder->match_remaining = 0;
- decoder->item_flags = 0;
- decoder->flags_remaining = 0;
-
- for (ta_uint32 i = 0; i < image->frame_size; i++) {
- image->pixels[i] = 0;
- }
-
- ta_free(image->scaled);
-
- image->scaled = 0;
- image->frames_decoded = 0;
-}
-
-static ta_bool decode_next_frame(ta_image_cache *image) {
- ta_image_decoder *decoder = &image->decoder;
- const ta_uint8 *stream = image->stream;
- ta_uint32 stream_length = image->stream_length;
- ta_uint32 frame_size = image->frame_size;
- ta_uint32 position = 0;
-
- while (position < frame_size) {
- ta_uint8 value;
-
- if (decoder->match_remaining != 0) {
- ta_uint32 source_position = (decoder->window_position + TAIF_WINDOW_SIZE - decoder->match_distance) & (TAIF_WINDOW_SIZE - 1);
-
- value = decoder->window[source_position];
- decoder->match_remaining--;
- } else {
- if (decoder->flags_remaining == 0) {
- if (decoder->stream_position >= stream_length) {
- return ta_false;
- }
-
- decoder->item_flags = stream[decoder->stream_position++];
- decoder->flags_remaining = 8;
- }
-
- ta_bool literal = (decoder->item_flags & 1) != 0;
-
- decoder->item_flags >>= 1;
- decoder->flags_remaining--;
-
- if (literal) {
- if (decoder->stream_position >= stream_length) {
- return ta_false;
- }
-
- value = stream[decoder->stream_position++];
- } else {
- if (stream_length - decoder->stream_position < 2) {
- return ta_false;
- }
-
- ta_uint8 b0 = stream[decoder->stream_position++];
- ta_uint8 b1 = stream[decoder->stream_position++];
-
- decoder->match_distance = ((((ta_uint32)b1 >> 4) << 8) | b0) + 1;
- decoder->match_remaining = (b1 & 0x0f) + 3;
-
- if (decoder->match_distance > decoder->filled) {
- return ta_false;
- }
-
- continue;
- }
- }
-
- image->pixels[position++] ^= value;
- decoder->window[decoder->window_position] = value;
- decoder->window_position = (decoder->window_position + 1) & (TAIF_WINDOW_SIZE - 1);
-
- if (decoder->filled < TAIF_WINDOW_SIZE) {
- decoder->filled++;
- }
- }
-
- ta_bool eight_bpp = (image->flags & TAIF_FLAG_8BPP) != 0;
- ta_bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
- ta_uint32 max_index = transparent ? image->palette_entries : (ta_uint32)image->palette_entries - 1;
- ta_uint32 full_bytes = eight_bpp ? image->width : image->width / 2;
- ta_bool odd_column = !eight_bpp && (image->width & 1) != 0;
-
- for (ta_uint32 row = 0; row < image->height; row++) {
- const ta_uint8 *line = image->pixels + (ta_size)row * image->stride;
-
- for (ta_uint32 i = 0; i < full_bytes; i++) {
- if (eight_bpp) {
- if (line[i] > max_index) {
- return ta_false;
- }
- } else if ((ta_uint32)(line[i] >> 4) > max_index || (ta_uint32)(line[i] & 0x0f) > max_index) {
- return ta_false;
- }
- }
-
- if (odd_column && (ta_uint32)(line[full_bytes] >> 4) > max_index) {
- return ta_false;
- }
- }
-
- ta_free(image->scaled);
-
- image->scaled = 0;
- image->frames_decoded++;
-
- return ta_true;
-}
-
-static ta_image_cache *find_image(const ta_uint8 *data, ta_uint32 length) {
- for (ta_image_cache *image = image_cache; image != 0; image = image->next) {
- if (image->data == data && image->length == length) {
- return image;
- }
- }
-
- return 0;
-}
-
-ta_bool ta_image_init(const ta_uint8 *data, ta_uint32 length) {
- if (find_image(data, length) != 0) {
- return ta_true;
- }
-
- ta_image_cache *image = (ta_image_cache *)ta_malloc(sizeof(*image));
- if (image == 0) {
- return ta_false;
- }
-
- *image = (ta_image_cache){0};
-
- if (!parse_image(data, length, image)) {
- ta_free(image);
- return ta_false;
- }
-
- if (!decode_next_frame(image)) {
- ta_free(image->pixels);
- ta_free(image);
- return ta_false;
- }
-
- image->next = image_cache;
- image_cache = image;
- return ta_true;
-}
-
-ta_bool ta_image_get_size(const ta_uint8 *data, ta_uint32 length, ta_uint32 *width, ta_uint32 *height) {
- ta_image_cache *image = find_image(data, length);
- if (image == 0 || width == 0 || height == 0) {
- return ta_false;
- }
-
- *width = image->width;
- *height = image->height;
-
- return ta_true;
-}
-
-static ta_image_cache *get_image(const ta_uint8 *data, ta_uint32 length, ta_uint16 frame) {
- if (!ta_image_init(data, length)) {
- return 0;
- }
-
- ta_image_cache *image = find_image(data, length);
- if (frame >= image->frame_count) {
- return 0;
- }
-
- ta_uint32 wanted = (ta_uint32)frame + 1;
-
- if (image->frames_decoded > wanted) {
- decoder_reset(image);
- }
-
- while (image->frames_decoded < wanted) {
- if (!decode_next_frame(image)) {
- decoder_reset(image);
- return 0;
- }
- }
-
- return image;
-}
-
-static ta_uint8 get_pixel_index(const ta_image_cache *image, ta_uint32 x, ta_uint32 y) {
- if ((image->flags & TAIF_FLAG_8BPP) != 0) {
- return image->pixels[y * image->stride + x];
- }
-
- ta_uint8 packed = image->pixels[y * image->stride + x / 2];
- return (x & 1) == 0 ? packed >> 4 : packed & 0x0f;
-}
-
-typedef struct {
- ta_uint16 first;
- ta_uint16 second;
- ta_uint8 fraction;
-} ta_image_axis;
-
-static ta_image_axis axis_position(ta_uint32 index, ta_uint32 count, ta_uint32 source_count) {
- ta_image_axis axis;
-
-#ifdef TA_DISABLE_BILINEAR_FILTERING
- axis.first = (ta_uint16)((ta_uint64)index * source_count / count);
- axis.second = axis.first;
- axis.fraction = 0;
-#else
- ta_uint64 position = ((ta_uint64)(2 * (ta_uint64)index + 1) * source_count * 128) / count;
-
- if (position <= 128) {
- axis.first = 0;
- axis.second = 0;
- axis.fraction = 0;
- } else {
- position -= 128;
-
- ta_uint32 first = (ta_uint32)(position >> 8);
-
- if (first >= source_count - 1) {
- axis.first = (ta_uint16)(source_count - 1);
- axis.second = axis.first;
- axis.fraction = 0;
- } else {
- axis.first = (ta_uint16)first;
- axis.second = (ta_uint16)(first + 1);
- axis.fraction = (ta_uint8)(position & 0xff);
- }
- }
-#endif
-
- return axis;
-}
-
-static ta_uint32 sample_image(const ta_image_cache *image, ta_image_axis column, ta_image_axis row, ta_uint32 *red, ta_uint32 *green, ta_uint32 *blue) {
- ta_uint32 weight_x0 = 256 - column.fraction;
- ta_uint32 weight_y0 = 256 - row.fraction;
- ta_bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
-
- ta_uint32 weights[4] = {
- weight_x0 * weight_y0,
- column.fraction * weight_y0,
- weight_x0 * row.fraction,
- column.fraction * row.fraction
- };
-
- ta_uint8 indices[4] = {
- get_pixel_index(image, column.first, row.first),
- get_pixel_index(image, column.second, row.first),
- get_pixel_index(image, column.first, row.second),
- get_pixel_index(image, column.second, row.second)
- };
-
- ta_uint32 red_sum = 0;
- ta_uint32 green_sum = 0;
- ta_uint32 blue_sum = 0;
- ta_uint32 alpha_sum = 0;
-
- for (ta_uint32 sample = 0; sample < 4; sample++) {
- if (transparent && indices[sample] == 0) {
- continue;
- }
-
- ta_uint32 palette_index = indices[sample] - (transparent ? 1 : 0);
-
- red_sum += (ta_uint32)image->palette[palette_index][0] * weights[sample];
- green_sum += (ta_uint32)image->palette[palette_index][1] * weights[sample];
- blue_sum += (ta_uint32)image->palette[palette_index][2] * weights[sample];
- alpha_sum += weights[sample];
- }
-
- *red = red_sum;
- *green = green_sum;
- *blue = blue_sum;
-
- return alpha_sum;
-}
-
-static void copy_bytes(ta_uint8 *destination, const ta_uint8 *source, ta_size count) {
- if ((((ta_size)destination | (ta_size)source) & 3) == 0) {
- ta_size words = count / 4;
-
- for (ta_size i = 0; i < words; i++) {
- ((ta_uint32 *)destination)[i] = ((const ta_uint32 *)source)[i];
- }
-
- destination += words * 4;
- source += words * 4;
- count &= 3;
- }
-
- while (count--) {
- *destination++ = *source++;
- }
-}
-
-static ta_bool prepare_scaled(ta_image_cache *image, ta_uint32 width, ta_uint32 height) {
- if (image->scaled != 0 && image->scaled_width == width && image->scaled_height == height && image->scaled_bpp == ta_display->bpp) {
- return ta_true;
- }
-
- ta_size bytes_per_pixel = ta_display->bpp / 8;
- ta_uint8 *scaled = (ta_uint8 *)ta_malloc((ta_size)width * height * bytes_per_pixel);
- ta_image_axis *columns = (ta_image_axis *)ta_malloc((ta_size)width * sizeof(*columns));
-
- if (scaled == 0 || columns == 0) {
- ta_free(scaled);
- ta_free(columns);
-
- return ta_false;
- }
-
- for (ta_uint32 column = 0; column < width; column++) {
- columns[column] = axis_position(column, width, image->width);
- }
-
- ta_uint8 *output = scaled;
-
- for (ta_uint32 y = 0; y < height; y++) {
- ta_image_axis row = axis_position(y, height, image->height);
-
- for (ta_uint32 x = 0; x < width; x++) {
- ta_uint32 red;
- ta_uint32 green;
- ta_uint32 blue;
-
- sample_image(image, columns[x], row, &red, &green, &blue);
-
- red = (red + 32768) >> 16;
- green = (green + 32768) >> 16;
- blue = (blue + 32768) >> 16;
-
- if (ta_display->bpp == 32) {
- *(ta_uint32 *)output = (red << 16) | (green << 8) | blue;
- output += 4;
- } else {
- *output++ = (ta_uint8)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
- }
- }
- }
-
- ta_free(columns);
- ta_free(image->scaled);
-
- image->scaled = scaled;
- image->scaled_width = width;
- image->scaled_height = height;
- image->scaled_bpp = (ta_uint8)ta_display->bpp;
-
- return ta_true;
-}
-
-ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, ta_uint16 frame) {
- if (data == 0 || length == 0 || x > TA_FIXED_ONE || y > TA_FIXED_ONE || width == 0 || height == 0 || width > TA_FIXED_ONE || height > TA_FIXED_ONE || (!ta_display->sprite_mode && ta_display->buffers[1] == 0) || (ta_display->sprite_mode && (!ta_display_sprites || ta_display->bpp != 32)) || (ta_display->bpp != 8 && ta_display->bpp != 32)) {
- return ta_false;
- }
-
- ta_uint32 pixel_x = (ta_uint32)(((ta_uint64)x * ta_display->width) >> 16);
- ta_uint32 pixel_y = (ta_uint32)(((ta_uint64)y * ta_display->height) >> 16);
- ta_uint32 pixel_width = (ta_uint32)(((ta_uint64)width * ta_display->width) >> 16);
- ta_uint32 pixel_height = (ta_uint32)(((ta_uint64)height * ta_display->height) >> 16);
-
- if (pixel_width == 0 || pixel_height == 0) {
- return ta_false;
- }
-
- ta_image_cache *image = get_image(data, length, frame);
- if (image == 0) {
- return ta_false;
- }
-
- ta_uint32 visible_width = 0;
- ta_uint32 visible_height = 0;
-
- if (pixel_x < ta_display->width) {
- visible_width = pixel_width > ta_display->width - pixel_x ? (ta_uint32)(ta_display->width - pixel_x) : pixel_width;
- }
-
- if (pixel_y < ta_display->height) {
- visible_height = pixel_height > ta_display->height - pixel_y ? (ta_uint32)(ta_display->height - pixel_y) : pixel_height;
- }
-
- if (visible_width == 0 || visible_height == 0) {
- return ta_true;
- }
-
- ta_bool transparent = (image->flags & TAIF_FLAG_TRANSPARENT) != 0;
-
- if (ta_display->sprite_mode) {
- ta_uint32 source_width = image->width;
- ta_uint32 source_height = image->height;
-
- if (visible_width < pixel_width) {
- source_width = (ta_uint32)(((ta_uint64)visible_width * image->width + pixel_width - 1) / pixel_width);
- }
-
- if (visible_height < pixel_height) {
- source_height = (ta_uint32)(((ta_uint64)visible_height * image->height + pixel_height - 1) / pixel_height);
- }
-
- ta_display_sprite *sprite = ta_display_sprite_create((ta_uint16)source_width, (ta_uint16)source_height);
- if (!sprite) {
- return ta_false;
- }
-
- sprite->x = (ta_uint16)pixel_x;
- sprite->y = (ta_uint16)pixel_y;
- sprite->draw_width = (ta_uint16)visible_width;
- sprite->draw_height = (ta_uint16)visible_height;
-
- ta_uint32 *pixels = (ta_uint32 *)sprite->pixels;
- ta_uint32 palette_offset = transparent ? 1 : 0;
-
- for (ta_uint32 image_y = 0; image_y < source_height; image_y++) {
- for (ta_uint32 image_x = 0; image_x < source_width; image_x++) {
- ta_uint8 index = get_pixel_index(image, image_x, image_y);
- ta_uint32 *pixel = &pixels[(ta_size)image_y * source_width + image_x];
-
- if (transparent && index == 0) {
- *pixel = 0;
- } else {
- const ta_uint8 *color = image->palette[index - palette_offset];
-
- *pixel = ((ta_uint32)color[0] << 24) | ((ta_uint32)color[1] << 16) | ((ta_uint32)color[2] << 8) | 0xff;
- }
- }
- }
-
- if (!ta_display_sprite_add(sprite)) {
- ta_free(sprite);
- return ta_false;
- }
-
- return ta_true;
- }
-
- if (!transparent && prepare_scaled(image, pixel_width, pixel_height)) {
- ta_size bytes_per_pixel = ta_display->bpp / 8;
-
- for (ta_uint32 image_y = 0; image_y < visible_height; image_y++) {
- const ta_uint8 *source = image->scaled + (ta_size)image_y * pixel_width * bytes_per_pixel;
- ta_uint8 *destination = (ta_uint8 *)ta_display->buffers[1] + ((ta_size)(pixel_y + image_y) * ta_display->width + pixel_x) * bytes_per_pixel;
-
- if (ta_display->bpp == 8) {
- copy_bytes(destination, source, visible_width);
- } else {
- const ta_uint32 *source32 = (const ta_uint32 *)source;
- ta_uint32 *destination32 = (ta_uint32 *)destination;
-
- for (ta_uint32 image_x = 0; image_x < visible_width; image_x++) {
- destination32[image_x] = (destination32[image_x] & 0xff000000u) | source32[image_x];
- }
- }
- }
-
- return ta_true;
- }
-
- ta_image_axis *columns = (ta_image_axis *)ta_malloc((ta_size)visible_width * sizeof(*columns));
- if (columns == 0) {
- return ta_false;
- }
-
- for (ta_uint32 image_x = 0; image_x < visible_width; image_x++) {
- columns[image_x] = axis_position(image_x, pixel_width, image->width);
- }
-
- for (ta_uint32 image_y = 0; image_y < visible_height; image_y++) {
- ta_image_axis row = axis_position(image_y, pixel_height, image->height);
- ta_size row_start = (ta_size)(pixel_y + image_y) * ta_display->width + pixel_x;
-
- for (ta_uint32 image_x = 0; image_x < visible_width; image_x++) {
- ta_uint32 red;
- ta_uint32 green;
- ta_uint32 blue;
- ta_uint32 alpha_sum = sample_image(image, columns[image_x], row, &red, &green, &blue);
-
- if (alpha_sum == 0) {
- continue;
- }
-
- ta_size destination_index = row_start + image_x;
- ta_uint32 inverse_alpha = 65536 - alpha_sum;
-
- if (ta_display->bpp == 32) {
- ta_uint32 *pixels = (ta_uint32 *)ta_display->buffers[1];
- ta_uint32 destination = pixels[destination_index];
-
- red = (red + ((destination >> 16) & 0xff) * inverse_alpha + 32768) >> 16;
- green = (green + ((destination >> 8) & 0xff) * inverse_alpha + 32768) >> 16;
- blue = (blue + (destination & 0xff) * inverse_alpha + 32768) >> 16;
-
- pixels[destination_index] = (destination & 0xff000000u) | (red << 16) | (green << 8) | blue;
- } else {
- ta_uint8 *pixels = (ta_uint8 *)ta_display->buffers[1];
- ta_uint32 destination = pixels[destination_index];
-
- red = (red + (((destination >> 5) & 7) * 255 / 7) * inverse_alpha + 32768) >> 16;
- green = (green + (((destination >> 2) & 7) * 255 / 7) * inverse_alpha + 32768) >> 16;
- blue = (blue + ((destination & 3) * 255 / 3) * inverse_alpha + 32768) >> 16;
-
- pixels[destination_index] = (ta_uint8)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
- }
- }
- }
-
- ta_free(columns);
-
- return ta_true;
-}
-
-ta_bool ta_image_render(const ta_uint8 *data, ta_uint32 length, ta_uint16 frame, ta_uint32 width, ta_uint32 height, ta_uint32 *pixels) {
- if (pixels == 0 || width == 0 || height == 0) {
- return ta_false;
- }
-
- ta_image_cache *image = get_image(data, length, frame);
- if (image == 0) {
- return ta_false;
- }
-
- ta_image_axis *columns = (ta_image_axis *)ta_malloc((ta_size)width * sizeof(*columns));
- if (columns == 0) {
- return ta_false;
- }
-
- for (ta_uint32 x = 0; x < width; x++) {
- columns[x] = axis_position(x, width, image->width);
- }
-
- for (ta_uint32 y = 0; y < height; y++) {
- ta_image_axis row = axis_position(y, height, image->height);
-
- for (ta_uint32 x = 0; x < width; x++) {
- ta_uint32 red;
- ta_uint32 green;
- ta_uint32 blue;
-
- sample_image(image, columns[x], row, &red, &green, &blue);
-
- red = (red + 32768) >> 16;
- green = (green + 32768) >> 16;
- blue = (blue + 32768) >> 16;
-
- pixels[(ta_size)y * width + x] = (red << 16) | (green << 8) | blue;
- }
- }
-
- ta_free(columns);
-
- return ta_true;
-}
-
-void ta_image_release(void) {
- while (image_cache != 0) {
- ta_image_cache *image = image_cache;
- image_cache = image->next;
- ta_free(image->pixels);
- ta_free(image->scaled);
- ta_free(image);
- }
-}
diff --git a/src/ta_keyboard.c b/src/ta_keyboard.c
@@ -1,47 +0,0 @@
-#include "ta_keyboard.h"
-
-ta_keyboards ta_keyboard;
-
-ta_bool ta_keyboard_is_down(ta_keyboard_keys key) {
- if (key == KEY_NONE) {
- return ta_false;
- }
-
- for (ta_size i = 0; i < TA_KEYBOARD_MAX_KEYS_DOWN; i++) {
- if (ta_keyboard.keys_down[i] == (ta_uint8)key) {
- return ta_true;
- }
- }
-
- return ta_false;
-}
-
-void ta_keyboard_key_down(ta_keyboard_keys key) {
- if (key == KEY_NONE || ta_keyboard_is_down(key)) {
- return;
- }
-
- for (ta_size i = 0; i < TA_KEYBOARD_MAX_KEYS_DOWN; i++) {
- if (ta_keyboard.keys_down[i] == KEY_NONE) {
- ta_keyboard.keys_down[i] = (ta_uint8)key;
-
- return;
- }
- }
-}
-
-void ta_keyboard_key_up(ta_keyboard_keys key) {
- for (ta_size i = 0; i < TA_KEYBOARD_MAX_KEYS_DOWN; i++) {
- if (ta_keyboard.keys_down[i] == (ta_uint8)key) {
- ta_keyboard.keys_down[i] = KEY_NONE;
-
- return;
- }
- }
-}
-
-void ta_keyboard_clear(void) {
- for (ta_size i = 0; i < TA_KEYBOARD_MAX_KEYS_DOWN; i++) {
- ta_keyboard.keys_down[i] = KEY_NONE;
- }
-}
diff --git a/src/ta_main.c b/src/ta_main.c
@@ -1,42 +0,0 @@
-#include "ta_api.h"
-#include "ta_main.h"
-#include "ta_vm.h"
-#include "ta_screen.h"
-
-void ta_main_init(void) {
- if (!ta_display_init() || (!ta_display->sprite_mode && !ta_fb_init(ta_display))) {
- ta_exit(1);
- }
-
- ta_screen_init();
-
- if (!ta_vm_load_file("game/tuxanci2.tab")) {
- ta_exit(1);
- }
-}
-
-ta_bool ta_main_update(void) {
- ta_bool drawn = ta_screen_update();
-
- if (!ta_vm_running) {
- ta_main_destroy();
- }
-
- if (!drawn) {
- return ta_false;
- }
-
- ta_fb_swap(&ta_fb);
-
- return ta_true;
-}
-
-void ta_main_destroy(void) {
- ta_int32 exit_code = ta_vm_exit_code();
-
- ta_screen_destroy();
- ta_vm_unload();
- ta_display_deinit();
-
- ta_exit(exit_code);
-}
diff --git a/src/ta_scene.c b/src/ta_scene.c
@@ -1,173 +0,0 @@
-#include "ta_scene.h"
-#include "ta_api.h"
-#include "ta_display.h"
-#include "ta_fb.h"
-#include "ta_image.h"
-
-typedef struct {
- const ta_uint8 *data;
- ta_text *text;
- ta_uint32 length;
- ta_uint32 x;
- ta_uint32 y;
- ta_uint32 width;
- ta_uint32 height;
- ta_uint16 frame;
- ta_uint8 sprites_after;
-} ta_scene_op;
-
-static ta_scene_op *ops[2];
-static ta_size counts[2];
-static ta_size capacities[2];
-static ta_uint8 current;
-static ta_bool valid;
-static ta_uint16 last_width;
-static ta_uint16 last_height;
-static ta_uint8 last_bpp;
-
-static ta_bool same(const ta_scene_op *a, const ta_scene_op *b) {
- return a->data == b->data
- && a->text == b->text
- && a->length == b->length
- && a->x == b->x
- && a->y == b->y
- && a->width == b->width
- && a->height == b->height
- && a->frame == b->frame;
-}
-
-static ta_scene_op *push(void) {
- if (counts[current] == capacities[current]) {
- ta_size new_capacity = capacities[current] + 1;
- ta_scene_op *grown = (ta_scene_op *)ta_malloc(new_capacity * sizeof(ta_scene_op));
-
- if (grown == TA_NULL) {
- return TA_NULL;
- }
-
- for (ta_size i = 0; i < counts[current]; i++) {
- grown[i] = ops[current][i];
- }
-
- ta_free(ops[current]);
-
- ops[current] = grown;
- capacities[current] = new_capacity;
- }
-
- ta_scene_op *op = &ops[current][counts[current]++];
-
- op->data = TA_NULL;
- op->text = TA_NULL;
- op->length = 0;
- op->x = 0;
- op->y = 0;
- op->width = 0;
- op->height = 0;
- op->frame = 0;
- op->sprites_after = 0;
-
- return op;
-}
-
-static void sprites_trim(ta_uint8 count) {
- while (ta_display_sprites && ta_display_sprites->count > count) {
- ta_display_sprite_free(ta_display_sprites->sprites[ta_display_sprites->count - 1]);
- }
-}
-
-void ta_scene_invalidate(void) {
- valid = ta_false;
-}
-
-void ta_scene_begin(void) {
- counts[current] = 0;
-
- if (ta_display->width != last_width || ta_display->height != last_height || ta_display->bpp != last_bpp) {
- last_width = ta_display->width;
- last_height = ta_display->height;
- last_bpp = ta_display->bpp;
- valid = ta_false;
- }
-}
-
-ta_bool ta_scene_image(const ta_uint8 *data, ta_uint32 length, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, ta_uint16 frame) {
- ta_scene_op *op = push();
-
- if (op == TA_NULL) {
- return ta_false;
- }
-
- op->data = data;
- op->length = length;
- op->x = x;
- op->y = y;
- op->width = width;
- op->height = height;
- op->frame = frame;
-
- return ta_true;
-}
-
-ta_bool ta_scene_text(ta_text *text, ta_uint32 x, ta_uint32 y) {
- ta_scene_op *op = push();
-
- if (op == TA_NULL) {
- return ta_false;
- }
-
- op->text = text;
- op->x = x;
- op->y = y;
-
- return ta_true;
-}
-
-ta_bool ta_scene_end(void) {
- ta_scene_op *now = ops[current];
- ta_scene_op *before = ops[current ^ 1];
- ta_size now_count = counts[current];
- ta_size before_count = counts[current ^ 1];
- ta_size prefix = 0;
-
- if (valid) {
- while (prefix < now_count && prefix < before_count && same(&now[prefix], &before[prefix])) {
- prefix++;
- }
-
- if (prefix == now_count && prefix == before_count) {
- return ta_false;
- }
- }
-
- ta_size start = ta_display->sprite_mode ? prefix : 0;
-
- if (start == 0) {
- ta_fb_clear(ta_display);
- } else {
- sprites_trim(before[start - 1].sprites_after);
-
- for (ta_size i = 0; i < start; i++) {
- now[i].sprites_after = before[i].sprites_after;
- }
- }
-
- for (ta_size i = start; i < now_count; i++) {
- ta_scene_op *op = &now[i];
-
- if (op->text != TA_NULL) {
- op->text->x = op->x;
- op->text->y = op->y;
- ta_text_draw(op->text);
- } else {
- ta_image_draw(op->data, op->length, op->x, op->y, op->width, op->height, op->frame);
- }
-
- op->sprites_after = ta_display_sprites ? ta_display_sprites->count : 0;
- }
-
- valid = ta_true;
- current ^= 1;
-
- return ta_true;
-}
diff --git a/src/ta_screen.c b/src/ta_screen.c
@@ -1,136 +0,0 @@
-#include "ta_screen.h"
-#include "ta_abi.h"
-#include "ta_image.h"
-#include "ta_vm.h"
-#include "ta_scene.h"
-#include "ta_api.h"
-
-#define TA_SCREEN_NONE (-1)
-#define TA_SCREEN_MAX_TRANSITIONS 16
-
-typedef struct {
- ta_uint32 init;
- ta_uint32 update;
- ta_uint32 destroy;
- ta_bool registered;
-} ta_screen_entry;
-
-static ta_screen_entry *screens;
-static ta_uint32 screen_count;
-static ta_int32 current_screen;
-static ta_int32 pending_screen;
-
-static void screens_clear(void) {
- if (screens != TA_NULL) {
- ta_free(screens);
- }
-
- screens = TA_NULL;
- screen_count = 0;
-}
-
-static ta_bool screens_grow(ta_uint32 new_count) {
- ta_screen_entry *grown = (ta_screen_entry *)ta_malloc((ta_size)new_count * sizeof(ta_screen_entry));
-
- if (grown == TA_NULL) {
- return ta_false;
- }
-
- for (ta_uint32 i = 0; i < screen_count; i++) {
- grown[i] = screens[i];
- }
-
- for (ta_uint32 i = screen_count; i < new_count; i++) {
- grown[i].init = 0;
- grown[i].update = 0;
- grown[i].destroy = 0;
- grown[i].registered = ta_false;
- }
-
- if (screens != TA_NULL) {
- ta_free(screens);
- }
-
- screens = grown;
- screen_count = new_count;
-
- return ta_true;
-}
-
-void ta_screen_init(void) {
- screens_clear();
-
- current_screen = TA_SCREEN_NONE;
- pending_screen = TA_SCREEN_NONE;
-}
-
-ta_bool ta_screen_register(ta_uint32 id, ta_uint32 init, ta_uint32 update, ta_uint32 destroy) {
- if (id == 0xFFFFFFFFu) {
- return ta_false;
- }
-
- if (id >= screen_count && !screens_grow(id + 1)) {
- return ta_false;
- }
-
- screens[id].init = init;
- screens[id].update = update;
- screens[id].destroy = destroy;
- screens[id].registered = ta_true;
-
- return ta_true;
-}
-
-ta_bool ta_screen_switch(ta_uint32 id) {
- if (id >= screen_count || !screens[id].registered) {
- return ta_false;
- }
-
- pending_screen = (ta_int32)id;
-
- return ta_true;
-}
-
-ta_bool ta_screen_update(void) {
- for (ta_uint32 transitions = 0; pending_screen != TA_SCREEN_NONE && transitions < TA_SCREEN_MAX_TRANSITIONS; transitions++) {
- ta_int32 next_screen = pending_screen;
-
- pending_screen = TA_SCREEN_NONE;
-
- if (current_screen != TA_SCREEN_NONE && screens[current_screen].destroy != 0) {
- if (!ta_vm_call(screens[current_screen].destroy, 0, 0, 0, TA_NULL)) {
- return ta_false;
- }
- }
-
- current_screen = next_screen;
-
- if (screens[current_screen].init != 0) {
- if (!ta_vm_call(screens[current_screen].init, 0, 0, 0, TA_NULL)) {
- return ta_false;
- }
- }
- }
-
- if (current_screen == TA_SCREEN_NONE || screens[current_screen].update == 0) {
- return ta_false;
- }
-
- ta_scene_begin();
-
- if (!ta_vm_call(screens[current_screen].update, 0, 0, 0, TA_NULL)) {
- return ta_false;
- }
-
- return ta_scene_end();
-}
-
-void ta_screen_destroy(void) {
- current_screen = TA_SCREEN_NONE;
- pending_screen = TA_SCREEN_NONE;
- ta_scene_invalidate();
-
- screens_clear();
-
- ta_image_release();
-}
diff --git a/src/ta_syscall.c b/src/ta_syscall.c
@@ -1,278 +0,0 @@
-#include "ta_syscall.h"
-#include "ta_abi.h"
-#include "ta_api.h"
-#include "ta_display.h"
-#include "ta_fb.h"
-#include "ta_image.h"
-#include "ta_keyboard.h"
-#include "ta_scene.h"
-#include "ta_screen.h"
-#include "ta_text.h"
-
-static ta_bool image_data(const ta_vm *vm, ta_uint32 address, const ta_uint8 **data, ta_uint32 *length) {
- *data = (const ta_uint8 *)ta_vm_translate_pointer(vm, address, 1);
-
- if (*data == TA_NULL) {
- return ta_false;
- }
-
- *length = vm->mem_size - address;
-
- return ta_true;
-}
-
-static ta_text **texts;
-static ta_uint32 text_count;
-
-static ta_text *text_get(ta_uint32 handle) {
- if (handle == 0 || handle > text_count) {
- return TA_NULL;
- }
-
- return texts[handle - 1];
-}
-
-static ta_bool text_slot_find(ta_uint32 *slot) {
- for (ta_uint32 i = 0; i < text_count; i++) {
- if (texts[i] == TA_NULL) {
- *slot = i;
-
- return ta_true;
- }
- }
-
- ta_text **grown = (ta_text **)ta_malloc((ta_size)(text_count + 1) * sizeof(ta_text *));
-
- if (grown == TA_NULL) {
- return ta_false;
- }
-
- for (ta_uint32 i = 0; i < text_count; i++) {
- grown[i] = texts[i];
- }
-
- grown[text_count] = TA_NULL;
-
- if (texts != TA_NULL) {
- ta_free(texts);
- }
-
- texts = grown;
- *slot = text_count;
- text_count++;
-
- return ta_true;
-}
-
-static ta_uint32 text_create(const char *string, ta_uint32 size, ta_uint32 color, ta_uint32 border_width, ta_uint32 border_color) {
- if (size == 0) {
- return 0;
- }
-
- ta_text *text = (ta_text *)ta_malloc(sizeof(ta_text));
-
- if (text == TA_NULL) {
- return 0;
- }
-
- text->text = string;
- text->x = 0;
- text->y = 0;
- text->size = (ta_uint8)size;
- text->red = (ta_uint8)((color >> 16) & 255);
- text->green = (ta_uint8)((color >> 8) & 255);
- text->blue = (ta_uint8)(color & 255);
- text->border_width = (ta_uint8)border_width;
- text->border_red = (ta_uint8)((border_color >> 16) & 255);
- text->border_green = (ta_uint8)((border_color >> 8) & 255);
- text->border_blue = (ta_uint8)(border_color & 255);
- text->width = 0;
- text->height = 0;
- text->sprite = TA_NULL;
-
- ta_text_rasterize(text);
-
- text->text = TA_NULL;
-
- if (!text->sprite) {
- ta_free(text);
-
- return 0;
- }
-
- ta_uint32 slot;
-
- if (!text_slot_find(&slot)) {
- ta_text_destroy(text);
- ta_free(text);
-
- return 0;
- }
-
- texts[slot] = text;
-
- return slot + 1;
-}
-
-static void text_destroy(ta_uint32 handle) {
- ta_text *text = text_get(handle);
-
- if (!text) {
- return;
- }
-
- ta_scene_invalidate();
- ta_text_destroy(text);
- ta_free(text);
- texts[handle - 1] = TA_NULL;
-}
-
-ta_vm_status ta_syscall(ta_vm *vm) {
- ta_uint32 *registers = vm->regs;
- ta_uint32 *args = ®isters[TA_VM_REG_A0];
- ta_uint32 result = 0;
-
- switch (registers[TA_VM_REG_A7]) {
- case TA_SYS_DISPLAY_WIDTH: {
- result = ta_display->width;
-
- break;
- }
- case TA_SYS_DISPLAY_HEIGHT: {
- result = ta_display->height;
-
- break;
- }
- case TA_SYS_DISPLAY_SPRITE_MODE: {
- result = ta_display->sprite_mode ? 1 : 0;
-
- break;
- }
- case TA_SYS_KEYBOARD_IS_DOWN: {
- result = args[0] < 256 && ta_keyboard_is_down((ta_keyboard_keys)args[0]) ? 1 : 0;
-
- break;
- }
- case TA_SYS_IMAGE_INIT: {
- const ta_uint8 *data;
- ta_uint32 length;
-
- if (!image_data(vm, args[0], &data, &length)) {
- return TA_VM_FAULT_MEMORY;
- }
-
- result = ta_image_init(data, length) ? 1 : 0;
-
- break;
- }
- case TA_SYS_IMAGE_DRAW: {
- const ta_uint8 *data;
- ta_uint32 length;
-
- if (!image_data(vm, args[0], &data, &length)) {
- return TA_VM_FAULT_MEMORY;
- }
-
- result = ta_scene_image(data, length, args[1], args[2], args[3], args[4], (ta_uint16)args[5]) ? 1 : 0;
-
- break;
- }
- case TA_SYS_IMAGE_GET_SIZE: {
- const ta_uint8 *data;
- ta_uint32 length;
- ta_uint32 width;
- ta_uint32 height;
-
- if (!image_data(vm, args[0], &data, &length)) {
- return TA_VM_FAULT_MEMORY;
- }
-
- if (!ta_image_get_size(data, length, &width, &height)) {
- break;
- }
-
- if (!ta_vm_write32(vm, args[1], width) || !ta_vm_write32(vm, args[2], height)) {
- return TA_VM_FAULT_MEMORY;
- }
-
- result = 1;
-
- break;
- }
- case TA_SYS_TEXT_CREATE: {
- const char *string = ta_vm_cstr(vm, args[0]);
-
- if (!string) {
- return TA_VM_FAULT_MEMORY;
- }
-
- result = text_create(string, args[1], args[2], args[3], args[4]);
-
- break;
- }
- case TA_SYS_TEXT_WIDTH: {
- ta_text *text = text_get(args[0]);
-
- result = text ? text->width : 0;
-
- break;
- }
- case TA_SYS_TEXT_HEIGHT: {
- ta_text *text = text_get(args[0]);
-
- result = text ? text->height : 0;
-
- break;
- }
- case TA_SYS_TEXT_DRAW: {
- ta_text *text = text_get(args[0]);
-
- if (text) {
- ta_scene_text(text, args[1], args[2]);
- }
-
- break;
- }
- case TA_SYS_TEXT_DESTROY: {
- text_destroy(args[0]);
-
- break;
- }
- case TA_SYS_SCREEN_REGISTER: {
- result = ta_screen_register(args[0], args[1], args[2], args[3]) ? 1 : 0;
-
- break;
- }
- case TA_SYS_SCREEN_SWITCH: {
- result = ta_screen_switch(args[0]) ? 1 : 0;
-
- break;
- }
- default: {
- return TA_VM_FAULT_ILLEGAL;
- }
- }
-
- registers[TA_VM_REG_A0] = result;
-
- return TA_VM_RUNNING;
-}
-
-void ta_syscall_reset(void) {
- ta_scene_invalidate();
- ta_image_release();
-
- for (ta_uint32 i = 0; i < text_count; i++) {
- if (texts[i] != TA_NULL) {
- ta_text_destroy(texts[i]);
- ta_free(texts[i]);
- }
- }
-
- if (texts != TA_NULL) {
- ta_free(texts);
- }
-
- texts = TA_NULL;
- text_count = 0;
-}
diff --git a/src/ta_text.c b/src/ta_text.c
@@ -1,1067 +0,0 @@
-#include "ta_text.h"
-#include "ta_api.h"
-#include "ta_files.h"
-
-#define ONE 65536
-#define MAX_COORD 16384
-#define MAX_SIZE 2048
-#define MAX_SEGMENTS 64
-
-typedef struct {
- ta_int32 x;
- ta_int32 y;
-} ta_text_pt;
-
-typedef struct {
- ta_int32 clip_x0;
- ta_int32 clip_y0;
- ta_int32 clip_x1;
- ta_int32 clip_y1;
- ta_int32 scale;
- ta_uint8 red;
- ta_uint8 green;
- ta_uint8 blue;
- ta_uint8 border_thickness;
- ta_uint8 border_red;
- ta_uint8 border_green;
- ta_uint8 border_blue;
- ta_uint32 *sprite_pixels;
- ta_int32 sprite_x;
- ta_int32 sprite_y;
- ta_int32 sprite_width;
-} ta_text_ctx;
-
-typedef struct {
- const ta_uint8 *glyf;
- const ta_uint8 *loca;
- const ta_uint8 *hmtx;
- const ta_uint8 *cmap;
- ta_uint32 glyf_length;
- ta_uint32 cmap_length;
- ta_uint32 units_per_em;
- ta_uint32 horizontal_metric_count;
- ta_uint32 glyph_count;
- ta_bool long_loca;
- ta_int32 ascent;
- ta_int32 descent;
- ta_int32 line_gap;
-} ta_text_font;
-
-static ta_text_font font;
-static int font_state;
-
-static ta_uint32 read_u16(const ta_uint8 *data) {
- return ((ta_uint32)data[0] << 8) | data[1];
-}
-
-static ta_int32 read_s16(const ta_uint8 *data) {
- ta_uint32 value = read_u16(data);
-
- if (value >= 0x8000) {
- return (ta_int32)value - 0x10000;
- }
-
- return (ta_int32)value;
-}
-
-static ta_uint32 read_u32(const ta_uint8 *data) {
- return (read_u16(data) << 16) | read_u16(data + 2);
-}
-
-static ta_uint64 unsigned_divide64(ta_uint64 numerator, ta_uint32 denominator) {
-#if TA_BITS == 64
- return numerator / denominator;
-#else
- if ((numerator >> 32) == 0) {
- return (ta_uint32)numerator / denominator;
- }
-
- ta_uint64 quotient = 0;
- ta_uint64 remainder = 0;
-
- for (int bit = 63; bit >= 0; bit--) {
- remainder = (remainder << 1) | ((numerator >> bit) & 1);
-
- if (remainder >= denominator) {
- remainder -= denominator;
- quotient |= (ta_uint64)1 << bit;
- }
- }
-
- return quotient;
-#endif
-}
-
-static ta_int64 signed_divide64(ta_int64 numerator, ta_int32 denominator) {
- ta_bool negative = ta_false;
- ta_uint64 unsigned_numerator;
- ta_uint32 unsigned_denominator;
-
- if (numerator < 0) {
- negative = !negative;
- unsigned_numerator = (ta_uint64)(-numerator);
- } else {
- unsigned_numerator = (ta_uint64)numerator;
- }
-
- if (denominator < 0) {
- negative = !negative;
- unsigned_denominator = (ta_uint32)(-denominator);
- } else {
- unsigned_denominator = (ta_uint32)denominator;
- }
-
- ta_int64 quotient = (ta_int64)unsigned_divide64(unsigned_numerator, unsigned_denominator);
-
- return negative ? -quotient : quotient;
-}
-
-static ta_int32 fixed_multiply(ta_int32 a, ta_int32 b) {
- return (ta_int32)(((ta_int64)a * b) >> 16);
-}
-
-static const ta_uint8 *find_table(const ta_uint8 *data, ta_uint32 length, const char *tag, ta_uint32 *out_length) {
- if (length < 12) {
- return 0;
- }
-
- ta_uint32 table_count = read_u16(data + 4);
-
- if (length < 12 + table_count * 16) {
- return 0;
- }
-
- for (ta_uint32 i = 0; i < table_count; i++) {
- const ta_uint8 *record = data + 12 + i * 16;
-
- if (record[0] == (ta_uint8)tag[0] && record[1] == (ta_uint8)tag[1] && record[2] == (ta_uint8)tag[2] && record[3] == (ta_uint8)tag[3]) {
- ta_uint32 offset = read_u32(record + 8);
- ta_uint32 table_length = read_u32(record + 12);
-
- if (offset > length || table_length > length - offset) {
- return 0;
- }
-
- *out_length = table_length;
-
- return data + offset;
- }
- }
-
- return 0;
-}
-
-static ta_bool font_load(void) {
- const ta_uint8 *font_data = ta_file_dejavu_sans;
- ta_uint32 font_length = ta_file_dejavu_sans_len;
-
- ta_uint32 head_length;
- ta_uint32 hhea_length;
- ta_uint32 maxp_length;
- ta_uint32 loca_length;
- ta_uint32 hmtx_length;
- ta_uint32 cmap_length;
-
- const ta_uint8 *head = find_table(font_data, font_length, "head", &head_length);
- const ta_uint8 *hhea = find_table(font_data, font_length, "hhea", &hhea_length);
- const ta_uint8 *maxp = find_table(font_data, font_length, "maxp", &maxp_length);
- const ta_uint8 *cmap = find_table(font_data, font_length, "cmap", &cmap_length);
-
- font.loca = find_table(font_data, font_length, "loca", &loca_length);
- font.hmtx = find_table(font_data, font_length, "hmtx", &hmtx_length);
- font.glyf = find_table(font_data, font_length, "glyf", &font.glyf_length);
-
- if (!head || !hhea || !maxp || !font.loca || !font.hmtx || !font.glyf || !cmap) {
- return ta_false;
- }
-
- if (head_length < 54 || hhea_length < 36 || maxp_length < 6 || cmap_length < 4) {
- return ta_false;
- }
-
- font.units_per_em = read_u16(head + 18);
- font.long_loca = read_s16(head + 50) != 0;
- font.ascent = read_s16(hhea + 4);
- font.descent = read_s16(hhea + 6);
- font.line_gap = read_s16(hhea + 8);
- font.horizontal_metric_count = read_u16(hhea + 34);
- font.glyph_count = read_u16(maxp + 4);
-
- if (font.units_per_em == 0 || font.horizontal_metric_count == 0 || font.glyph_count == 0) {
- return ta_false;
- }
-
- if (font.ascent - font.descent + font.line_gap <= 0) {
- return ta_false;
- }
-
- if (loca_length < (font.glyph_count + 1) * (font.long_loca ? 4u : 2u)) {
- return ta_false;
- }
-
- if (hmtx_length < font.horizontal_metric_count * 4) {
- return ta_false;
- }
-
- const ta_uint8 *best_subtable = 0;
- ta_uint32 best_length = 0;
- ta_uint32 subtable_count = read_u16(cmap + 2);
-
- if (cmap_length < 4 + subtable_count * 8) {
- return ta_false;
- }
-
- for (ta_uint32 i = 0; i < subtable_count; i++) {
- const ta_uint8 *record = cmap + 4 + i * 8;
- ta_uint32 platform = read_u16(record);
- ta_uint32 encoding = read_u16(record + 2);
- ta_uint32 offset = read_u32(record + 4);
-
- if (offset > cmap_length || cmap_length - offset < 16) {
- continue;
- }
-
- if (read_u16(cmap + offset) != 4) {
- continue;
- }
-
- if (platform == 3 && encoding == 1) {
- best_subtable = cmap + offset;
- best_length = cmap_length - offset;
-
- break;
- }
-
- if (platform == 0 && !best_subtable) {
- best_subtable = cmap + offset;
- best_length = cmap_length - offset;
- }
- }
-
- if (!best_subtable) {
- return ta_false;
- }
-
- ta_uint32 segment_count_x2 = read_u16(best_subtable + 6);
-
- if (best_length < 16 + segment_count_x2 * 4) {
- return ta_false;
- }
-
- font.cmap = best_subtable;
- font.cmap_length = best_length;
-
- return ta_true;
-}
-
-static ta_bool font_ready(void) {
- if (font_state == 0) {
- font_state = font_load() ? 1 : -1;
- }
-
- return font_state == 1;
-}
-
-static ta_uint32 glyph_for(ta_uint32 character) {
- if (character > 0xFFFF) {
- return 0;
- }
-
- const ta_uint8 *table = font.cmap;
- ta_uint32 segment_count_x2 = read_u16(table + 6);
- const ta_uint8 *end_codes = table + 14;
- const ta_uint8 *start_codes = table + 16 + segment_count_x2;
- const ta_uint8 *deltas = table + 16 + segment_count_x2 * 2;
- const ta_uint8 *range_offsets = table + 16 + segment_count_x2 * 3;
-
- for (ta_uint32 i = 0; i < segment_count_x2 / 2; i++) {
- if (character > read_u16(end_codes + i * 2)) {
- continue;
- }
-
- ta_uint32 start = read_u16(start_codes + i * 2);
-
- if (character < start) {
- return 0;
- }
-
- ta_uint32 delta = read_u16(deltas + i * 2);
- ta_uint32 range_offset = read_u16(range_offsets + i * 2);
-
- if (range_offset == 0) {
- return (character + delta) & 0xFFFF;
- }
-
- const ta_uint8 *entry = range_offsets + i * 2 + range_offset + (character - start) * 2;
-
- if ((ta_size)(entry + 2 - font.cmap) > font.cmap_length) {
- return 0;
- }
-
- ta_uint32 glyph = read_u16(entry);
-
- return glyph ? (glyph + delta) & 0xFFFF : 0;
- }
-
- return 0;
-}
-
-static ta_uint32 advance_of(ta_uint32 glyph_id) {
- if (glyph_id >= font.horizontal_metric_count) {
- glyph_id = font.horizontal_metric_count - 1;
- }
-
- return read_u16(font.hmtx + glyph_id * 4);
-}
-
-static const ta_uint8 *glyph_data(ta_uint32 glyph_id, ta_uint32 *length) {
- if (glyph_id >= font.glyph_count) {
- return 0;
- }
-
- ta_uint32 begin;
- ta_uint32 end;
-
- if (font.long_loca) {
- begin = read_u32(font.loca + glyph_id * 4);
- end = read_u32(font.loca + glyph_id * 4 + 4);
- } else {
- begin = read_u16(font.loca + glyph_id * 2) * 2;
- end = read_u16(font.loca + glyph_id * 2 + 2) * 2;
- }
-
- if (end <= begin || end > font.glyf_length || end - begin < 10) {
- return 0;
- }
-
- *length = end - begin;
-
- return font.glyf + begin;
-}
-
-static void raster_line(ta_int32 *coverage, ta_int32 stride, ta_int32 width, ta_int32 height, ta_int32 start_x, ta_int32 start_y, ta_int32 end_x, ta_int32 end_y) {
- if (start_y == end_y) {
- return;
- }
-
- ta_int32 direction = 1;
-
- if (start_y > end_y) {
- ta_int32 temp;
-
- direction = -1;
-
- temp = start_x;
- start_x = end_x;
- end_x = temp;
-
- temp = start_y;
- start_y = end_y;
- end_y = temp;
- }
-
- if (end_y <= 0 || start_y >= height * ONE) {
- return;
- }
-
- ta_int32 delta_x = end_x - start_x;
- ta_int32 delta_y_total = end_y - start_y;
- ta_int32 first_row = start_y < 0 ? 0 : (start_y >> 16);
- ta_int32 last_row = (end_y + (ONE - 1)) >> 16;
-
- if (last_row > height) {
- last_row = height;
- }
-
- ta_int32 current_x = start_x;
-
- if (start_y < 0) {
- current_x = start_x + (ta_int32)signed_divide64((ta_int64)delta_x * (0 - start_y), delta_y_total);
- }
-
- ta_int32 max_x = width * ONE;
-
- for (ta_int32 row_index = first_row; row_index < last_row; row_index++) {
- ta_int32 row_top = row_index * ONE;
- ta_int32 row_bottom = row_top + ONE;
- ta_int32 segment_top = start_y > row_top ? start_y : row_top;
- ta_int32 segment_bottom = end_y < row_bottom ? end_y : row_bottom;
- ta_int32 segment_height = segment_bottom - segment_top;
- ta_int32 next_x;
-
- if (segment_bottom == end_y) {
- next_x = end_x;
- } else {
- next_x = start_x + (ta_int32)signed_divide64((ta_int64)delta_x * (segment_bottom - start_y), delta_y_total);
- }
-
- ta_int32 signed_height = segment_height * direction;
- ta_int32 left_x = current_x < next_x ? current_x : next_x;
- ta_int32 right_x = current_x < next_x ? next_x : current_x;
-
- if (left_x < 0) {
- left_x = 0;
- }
-
- if (right_x < 0) {
- right_x = 0;
- }
-
- if (left_x > max_x) {
- left_x = max_x;
- }
-
- if (right_x > max_x) {
- right_x = max_x;
- }
-
- ta_int32 first_cell = left_x >> 16;
- ta_int32 last_cell = (right_x + (ONE - 1)) >> 16;
- ta_int32 *row = coverage + row_index * stride;
-
- if (last_cell <= first_cell + 1) {
- ta_int32 middle_offset = ((left_x + right_x) >> 1) - (first_cell << 16);
-
- row[first_cell] += signed_height - fixed_multiply(signed_height, middle_offset);
- row[first_cell + 1] += fixed_multiply(signed_height, middle_offset);
- } else {
- ta_int32 span = right_x - left_x;
- ta_int32 first_fraction = left_x - (first_cell << 16);
- ta_int32 first_remaining = ONE - first_fraction;
- ta_int32 last_fraction = right_x - ((last_cell - 1) << 16);
- ta_int32 first_area = (ta_int32)unsigned_divide64((ta_uint64)first_remaining * (ta_uint64)first_remaining, (ta_uint32)span * 2);
- ta_int32 last_area = (ta_int32)unsigned_divide64((ta_uint64)last_fraction * (ta_uint64)last_fraction, (ta_uint32)span * 2);
-
- row[first_cell] += fixed_multiply(signed_height, first_area);
-
- if (last_cell == first_cell + 2) {
- row[first_cell + 1] += fixed_multiply(signed_height, ONE - first_area - last_area);
- } else {
- ta_int32 slope_step = (ta_int32)unsigned_divide64((ta_uint64)ONE << 16, (ta_uint32)span);
- ta_int32 second_area = (ta_int32)unsigned_divide64((ta_uint64)(ONE + ONE / 2 - first_fraction) << 16, (ta_uint32)span);
-
- row[first_cell + 1] += fixed_multiply(signed_height, second_area - first_area);
-
- for (ta_int32 cell = first_cell + 2; cell < last_cell - 1; cell++) {
- row[cell] += fixed_multiply(signed_height, slope_step);
- }
-
- ta_int32 final_area = second_area + (last_cell - first_cell - 3) * slope_step;
-
- row[last_cell - 1] += fixed_multiply(signed_height, ONE - final_area - last_area);
- }
-
- row[last_cell] += fixed_multiply(signed_height, last_area);
- }
-
- current_x = next_x;
- }
-}
-
-static void raster_quad(ta_int32 *coverage, ta_int32 stride, ta_int32 width, ta_int32 height, ta_text_pt start, ta_text_pt control, ta_text_pt end) {
- ta_int32 second_diff_x = start.x - 2 * control.x + end.x;
- ta_int32 second_diff_y = start.y - 2 * control.y + end.y;
-
- if (second_diff_x < 0) {
- second_diff_x = -second_diff_x;
- }
-
- if (second_diff_y < 0) {
- second_diff_y = -second_diff_y;
- }
-
- ta_int32 deviation = (second_diff_x + second_diff_y) >> 2;
- ta_int32 segment_count = 1;
-
- while (segment_count < MAX_SEGMENTS && deviation > (ONE / 16) * segment_count * segment_count) {
- segment_count++;
- }
-
- ta_text_pt previous = start;
-
- for (ta_int32 i = 1; i <= segment_count; i++) {
- ta_text_pt current;
-
- if (i == segment_count) {
- current = end;
- } else {
- ta_int32 t = (ta_int32)((ta_uint32)i * ONE / (ta_uint32)segment_count);
- ta_int32 one_minus_t = ONE - t;
- ta_int64 weight_start = (ta_int64)one_minus_t * one_minus_t;
- ta_int64 weight_ctrl = (ta_int64)2 * one_minus_t * t;
- ta_int64 weight_end = (ta_int64)t * t;
-
- current.x = (ta_int32)((weight_start * start.x + weight_ctrl * control.x + weight_end * end.x) >> 32);
- current.y = (ta_int32)((weight_start * start.y + weight_ctrl * control.y + weight_end * end.y) >> 32);
- }
-
- raster_line(coverage, stride, width, height, previous.x, previous.y, current.x, current.y);
- previous = current;
- }
-}
-
-#define DIV255(t) ((((t) + 128) + (((t) + 128) >> 8)) >> 8)
-
-static ta_uint32 blend_channel(ta_uint32 destination, ta_uint32 source, ta_uint32 alpha) {
- ta_uint32 total = destination * (255 - alpha) + source * alpha;
-
- return DIV255(total);
-}
-
-static void put_pixel(const ta_text_ctx *ctx, ta_int32 pixel_x, ta_int32 pixel_y, ta_uint32 alpha) {
- ta_size index = (ta_size)(pixel_y - ctx->sprite_y) * (ta_size)ctx->sprite_width + (ta_size)(pixel_x - ctx->sprite_x);
- ta_uint32 destination = ctx->sprite_pixels[index];
- ta_uint32 destination_alpha = destination & 255;
- ta_uint32 inverse_alpha = 255 - alpha;
- ta_uint32 output_alpha = alpha + (destination_alpha * inverse_alpha + 127) / 255;
-
- if (output_alpha == 0) {
- return;
- }
-
- ta_uint32 destination_red = (destination >> 24) & 255;
- ta_uint32 destination_green = (destination >> 16) & 255;
- ta_uint32 destination_blue = (destination >> 8) & 255;
- ta_uint32 output_red = ((ta_uint32)ctx->red * alpha * 255 + destination_red * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
- ta_uint32 output_green = ((ta_uint32)ctx->green * alpha * 255 + destination_green * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
- ta_uint32 output_blue = ((ta_uint32)ctx->blue * alpha * 255 + destination_blue * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
-
- ctx->sprite_pixels[index] = (output_red << 24) | (output_green << 16) | (output_blue << 8) | output_alpha;
-}
-
-static void blit_pixel(ta_size index, ta_uint32 red, ta_uint32 green, ta_uint32 blue, ta_uint32 alpha) {
- if (ta_display->bpp == 32) {
- ta_uint32 *pixel = (ta_uint32 *)ta_display->buffers[1] + index;
- ta_uint32 destination = *pixel;
-
- *pixel = (destination & 0xFF000000u) | (blend_channel((destination >> 16) & 255, red, alpha) << 16) | (blend_channel((destination >> 8) & 255, green, alpha) << 8) | blend_channel(destination & 255, blue, alpha);
- } else {
- ta_uint8 *pixel = (ta_uint8 *)ta_display->buffers[1] + index;
- ta_uint32 destination = *pixel;
- ta_uint32 dest_red = (destination >> 5) & 7;
- ta_uint32 dest_green = (destination >> 2) & 7;
- ta_uint32 dest_blue = destination & 3;
-
- dest_red = (dest_red << 5) | (dest_red << 2) | (dest_red >> 1);
- dest_green = (dest_green << 5) | (dest_green << 2) | (dest_green >> 1);
- dest_blue = dest_blue * 85;
-
- red = blend_channel(dest_red, red, alpha);
- green = blend_channel(dest_green, green, alpha);
- blue = blend_channel(dest_blue, blue, alpha);
-
- *pixel = (ta_uint8)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
- }
-}
-
-static void emit_pixel(const ta_text_ctx *ctx, ta_int32 pixel_x, ta_int32 pixel_y, ta_uint32 alpha) {
- put_pixel(ctx, pixel_x, pixel_y, alpha);
-}
-
-static ta_text_pt transform_point(ta_int32 font_x, ta_int32 font_y, ta_int32 origin_x, ta_int32 origin_y, ta_int32 scale) {
- ta_text_pt point;
-
- point.x = origin_x + (ta_int32)((ta_int64)font_x * scale);
- point.y = origin_y - (ta_int32)((ta_int64)font_y * scale);
-
- return point;
-}
-
-static void draw_glyph(const ta_text_ctx *ctx, ta_uint32 glyph_id, ta_int32 pen_x, ta_int32 baseline_y) {
- ta_uint32 glyph_length;
- const ta_uint8 *glyph = glyph_data(glyph_id, &glyph_length);
-
- if (!glyph) {
- return;
- }
-
- ta_int32 contour_count = read_s16(glyph);
-
- if (contour_count <= 0) {
- return;
- }
-
- ta_int32 scale = ctx->scale;
- ta_int32 glyph_left = pen_x + (ta_int32)((ta_int64)read_s16(glyph + 2) * scale);
- ta_int32 glyph_right = pen_x + (ta_int32)((ta_int64)read_s16(glyph + 6) * scale);
- ta_int32 glyph_top = baseline_y - (ta_int32)((ta_int64)read_s16(glyph + 8) * scale);
- ta_int32 glyph_bottom = baseline_y - (ta_int32)((ta_int64)read_s16(glyph + 4) * scale);
- ta_int32 region_x0 = glyph_left >> 16;
- ta_int32 region_x1 = (glyph_right + (ONE - 1)) >> 16;
- ta_int32 region_y0 = glyph_top >> 16;
- ta_int32 region_y1 = (glyph_bottom + (ONE - 1)) >> 16;
-
- if (region_x0 < ctx->clip_x0) {
- region_x0 = ctx->clip_x0;
- }
-
- if (region_y0 < ctx->clip_y0) {
- region_y0 = ctx->clip_y0;
- }
-
- if (region_x1 > ctx->clip_x1) {
- region_x1 = ctx->clip_x1;
- }
-
- if (region_y1 > ctx->clip_y1) {
- region_y1 = ctx->clip_y1;
- }
-
- if (region_x0 >= region_x1 || region_y0 >= region_y1) {
- return;
- }
-
- ta_int32 region_width = region_x1 - region_x0;
- ta_int32 region_height = region_y1 - region_y0;
- ta_int32 stride = region_width + 2;
- ta_size coverage_bytes = (ta_size)stride * (ta_size)region_height * sizeof(ta_int32);
- const ta_uint8 *end = glyph + glyph_length;
- const ta_uint8 *cursor = glyph + 10;
-
- if ((ta_size)(end - cursor) < (ta_size)contour_count * 2 + 2) {
- return;
- }
-
- ta_uint32 point_count = read_u16(cursor + (contour_count - 1) * 2) + 1;
- const ta_uint8 *contour_ends = cursor;
-
- cursor += contour_count * 2;
- cursor += 2 + read_u16(cursor);
-
- if (cursor > end || point_count > 4096) {
- return;
- }
-
- ta_size points_bytes = (ta_size)point_count * 5;
- void *memory = ta_malloc(points_bytes + coverage_bytes + 8);
-
- if (!memory) {
- return;
- }
-
- ta_int16 *point_x = (ta_int16 *)memory;
- ta_int16 *point_y = point_x + point_count;
- ta_uint8 *flags = (ta_uint8 *)(point_y + point_count);
- ta_int32 *coverage = (ta_int32 *)(((ta_size)(flags + point_count) + 7) & ~(ta_size)7);
- ta_bool parse_ok = ta_true;
-
- for (ta_uint32 i = 0; i < point_count && parse_ok;) {
- if (cursor >= end) {
- parse_ok = ta_false;
-
- break;
- }
-
- ta_uint8 flag = *cursor++;
-
- flags[i++] = flag;
-
- if (flag & 8) {
- if (cursor >= end) {
- parse_ok = ta_false;
-
- break;
- }
-
- ta_uint32 repeat_count = *cursor++;
-
- while (repeat_count > 0 && i < point_count) {
- flags[i++] = flag;
- repeat_count--;
- }
- }
- }
-
- for (ta_uint32 pass = 0; pass < 2 && parse_ok; pass++) {
- ta_int16 *output = pass ? point_y : point_x;
- ta_uint8 short_bit = pass ? 4 : 2;
- ta_uint8 same_bit = pass ? 32 : 16;
- ta_int32 value = 0;
-
- for (ta_uint32 i = 0; i < point_count; i++) {
- ta_uint8 flag = flags[i];
-
- if (flag & short_bit) {
- if (cursor >= end) {
- parse_ok = ta_false;
-
- break;
- }
-
- ta_int32 delta = *cursor++;
-
- value += (flag & same_bit) ? delta : -delta;
- } else if (!(flag & same_bit)) {
- if (end - cursor < 2) {
- parse_ok = ta_false;
-
- break;
- }
-
- value += read_s16(cursor);
- cursor += 2;
- }
-
- output[i] = (ta_int16)value;
- }
- }
-
- if (!parse_ok) {
- ta_free(memory);
-
- return;
- }
-
- for (ta_size i = 0; i < coverage_bytes / sizeof(ta_int32); i++) {
- coverage[i] = 0;
- }
-
- ta_int32 origin_x = pen_x - region_x0 * ONE;
- ta_int32 origin_y = baseline_y - region_y0 * ONE;
-
-#define POINT(index) transform_point((ta_int32)point_x[index], (ta_int32)point_y[index], origin_x, origin_y, scale)
-
- ta_uint32 contour_first = 0;
-
- for (ta_int32 contour = 0; contour < contour_count; contour++) {
- ta_uint32 contour_last = read_u16(contour_ends + contour * 2);
- ta_uint32 contour_start = contour_first;
-
- contour_first = contour_last + 1;
-
- if (contour_last < contour_start || contour_last >= point_count || contour_last - contour_start < 1) {
- continue;
- }
-
- ta_uint32 contour_points = contour_last - contour_start + 1;
-
- ta_text_pt start_point;
- ta_uint32 begin;
- ta_uint32 count;
-
- if (flags[contour_start] & 1) {
- start_point = POINT(contour_start);
- begin = contour_start + 1;
- count = contour_points - 1;
- } else if (flags[contour_last] & 1) {
- start_point = POINT(contour_last);
- begin = contour_start;
- count = contour_points - 1;
- } else {
- ta_text_pt first_point = POINT(contour_start);
- ta_text_pt last_point = POINT(contour_last);
- start_point.x = (first_point.x >> 1) + (last_point.x >> 1);
- start_point.y = (first_point.y >> 1) + (last_point.y >> 1);
- begin = contour_start;
- count = contour_points;
- }
-
- ta_text_pt current = start_point;
- ta_text_pt control = start_point;
- ta_bool has_control = ta_false;
-
- for (ta_uint32 k = 0; k < count; k++) {
- ta_uint32 index = begin + k;
- ta_text_pt point = POINT(index);
-
- if (flags[index] & 1) {
- if (has_control) {
- raster_quad(coverage, stride, region_width, region_height, current, control, point);
- } else {
- raster_line(coverage, stride, region_width, region_height, current.x, current.y, point.x, point.y);
- }
-
- current = point;
- has_control = ta_false;
- } else if (has_control) {
- ta_text_pt midpoint;
-
- midpoint.x = (control.x >> 1) + (point.x >> 1);
- midpoint.y = (control.y >> 1) + (point.y >> 1);
-
- raster_quad(coverage, stride, region_width, region_height, current, control, midpoint);
-
- current = midpoint;
- control = point;
- } else {
- control = point;
- has_control = ta_true;
- }
- }
-
- if (has_control) {
- raster_quad(coverage, stride, region_width, region_height, current, control, start_point);
- } else {
- raster_line(coverage, stride, region_width, region_height, current.x, current.y, start_point.x, start_point.y);
- }
- }
-
-#undef POINT
-
- for (ta_int32 row_index = 0; row_index < region_height; row_index++) {
- ta_int32 running_sum = 0;
- const ta_int32 *row = coverage + row_index * stride;
-
- for (ta_int32 column = 0; column < region_width; column++) {
- running_sum += row[column];
-
- ta_int32 amount = running_sum < 0 ? -running_sum : running_sum;
-
- if (amount > ONE) {
- amount = ONE;
- }
-
- ta_uint32 alpha = ((ta_uint32)amount * 255 + 0x8000) >> 16;
-
- if (alpha) {
- emit_pixel(ctx, region_x0 + column, region_y0 + row_index, alpha);
- }
- }
- }
-
- ta_free(memory);
-}
-
-static ta_bool printable(ta_uint32 character) {
- return character >= 32 && character != 127;
-}
-
-static ta_uint32 line_height_units(void) {
- return (ta_uint32)(font.ascent - font.descent + font.line_gap);
-}
-
-static void measure_units(const char *text, ta_uint32 *max_width, ta_uint32 *lines) {
- ta_uint32 current_width = 0;
- ta_uint32 best_width = 0;
- ta_uint32 line_count = 1;
-
- for (; *text; text++) {
- ta_uint32 character = (ta_uint8)*text;
-
- if (character == '\n') {
- if (current_width > best_width) {
- best_width = current_width;
- }
-
- current_width = 0;
- line_count++;
- } else if (printable(character)) {
- current_width += advance_of(glyph_for(character));
- }
- }
-
- if (current_width > best_width) {
- best_width = current_width;
- }
-
- *max_width = best_width;
- *lines = line_count;
-}
-
-static void draw_text_pass(const char *text, ta_int32 x, ta_int32 y, const ta_text_ctx *ctx) {
- ta_int32 scale = ctx->scale;
- ta_int32 ascent = (ta_int32)((ta_int64)font.ascent * scale);
- ta_int32 line_height = (ta_int32)((ta_int64)line_height_units() * scale);
- ta_int32 pen_x = x * ONE;
- ta_int32 baseline_y = y * ONE + ascent;
-
- for (; *text; text++) {
- ta_uint32 character = (ta_uint8)*text;
-
- if (character == '\n') {
- pen_x = x * ONE;
- baseline_y += line_height;
-
- if (baseline_y - ascent >= ctx->clip_y1 * ONE) {
- break;
- }
-
- continue;
- }
-
- if (!printable(character) || pen_x >= ctx->clip_x1 * ONE) {
- continue;
- }
-
- ta_uint32 glyph_id = glyph_for(character);
-
- draw_glyph(ctx, glyph_id, pen_x, baseline_y);
- pen_x += (ta_int32)((ta_int64)advance_of(glyph_id) * scale);
- }
-}
-
-static void draw_text_direct(const char *text, ta_int32 x, ta_int32 y, const ta_text_ctx *ctx) {
- if (ctx->border_thickness != 0) {
- ta_text_ctx border_ctx = *ctx;
-
- border_ctx.red = ctx->border_red;
- border_ctx.green = ctx->border_green;
- border_ctx.blue = ctx->border_blue;
- ta_int32 thickness = ctx->border_thickness;
-
- for (ta_int32 offset_y = -thickness; offset_y <= thickness; offset_y++) {
- for (ta_int32 offset_x = -thickness; offset_x <= thickness; offset_x++) {
- if (offset_x == 0 && offset_y == 0) {
- continue;
- }
-
- draw_text_pass(text, x + offset_x, y + offset_y, &border_ctx);
- }
- }
- }
-
- draw_text_pass(text, x, y, ctx);
-}
-
-void ta_text_rasterize(ta_text *text) {
- if (!text) {
- return;
- }
-
- ta_text_destroy(text);
-
- if (!text->text || text->size == 0 || !font_ready()) {
- return;
- }
-
- ta_uint32 units;
- ta_uint32 lines;
-
- measure_units(text->text, &units, &lines);
-
- ta_uint64 scale = unsigned_divide64((ta_uint64)text->size << 16, font.units_per_em);
- ta_uint64 text_width = (units * scale + (ONE - 1)) >> 16;
- ta_uint64 text_height = ((ta_uint64)lines * line_height_units() * scale + (ONE - 1)) >> 16;
- ta_uint32 border = text->border_width;
- ta_uint64 sprite_width = text_width + (ta_uint64)border * 2;
- ta_uint64 sprite_height = text_height + (ta_uint64)border * 2;
-
- if (text_width == 0 || text_height == 0 || sprite_width > MAX_COORD || sprite_height > MAX_COORD) {
- return;
- }
-
- ta_size pixel_count = (ta_size)sprite_width * (ta_size)sprite_height;
- ta_display_sprite *sprite = ta_display_sprite_create((ta_uint16)sprite_width, (ta_uint16)sprite_height);
-
- if (!sprite) {
- return;
- }
-
- ta_uint32 *pixels = (ta_uint32 *)sprite->pixels;
-
- for (ta_size i = 0; i < pixel_count; i++) {
- pixels[i] = 0;
- }
-
- ta_text_ctx ctx;
-
- ctx.scale = (ta_int32)scale;
- ctx.clip_x0 = 0;
- ctx.clip_y0 = 0;
- ctx.clip_x1 = (ta_int32)sprite_width;
- ctx.clip_y1 = (ta_int32)sprite_height;
- ctx.red = text->red;
- ctx.green = text->green;
- ctx.blue = text->blue;
- ctx.border_thickness = text->border_width;
- ctx.border_red = text->border_red;
- ctx.border_green = text->border_green;
- ctx.border_blue = text->border_blue;
- ctx.sprite_pixels = pixels;
- ctx.sprite_x = 0;
- ctx.sprite_y = 0;
- ctx.sprite_width = (ta_int32)sprite_width;
-
- draw_text_direct(text->text, (ta_int32)border, (ta_int32)border, &ctx);
-
- text->width = (ta_uint32)text_width;
- text->height = (ta_uint32)text_height;
- text->sprite = sprite;
-}
-
-void ta_text_draw(ta_text *text) {
- if (!text || !text->sprite) {
- return;
- }
-
- const ta_display_sprite *source = text->sprite;
- ta_int32 destination_x = (ta_int32)text->x - (ta_int32)text->border_width;
- ta_int32 destination_y = (ta_int32)text->y - (ta_int32)text->border_width;
- ta_int32 column_start = destination_x < 0 ? -destination_x : 0;
- ta_int32 row_start = destination_y < 0 ? -destination_y : 0;
- ta_int32 column_end = (ta_int32)source->width;
- ta_int32 row_end = (ta_int32)source->height;
-
- if (column_end > (ta_int32)ta_display->width - destination_x) {
- column_end = (ta_int32)ta_display->width - destination_x;
- }
-
- if (row_end > (ta_int32)ta_display->height - destination_y) {
- row_end = (ta_int32)ta_display->height - destination_y;
- }
-
- if (column_start >= column_end || row_start >= row_end) {
- return;
- }
-
- const ta_uint32 *source_pixels = (const ta_uint32 *)source->pixels;
-
- if (ta_display->sprite_mode) {
- if (!ta_display_sprites) {
- return;
- }
-
- ta_uint32 visible_width = (ta_uint32)(column_end - column_start);
- ta_uint32 visible_height = (ta_uint32)(row_end - row_start);
- ta_display_sprite *copy = ta_display_sprite_create((ta_uint16)visible_width, (ta_uint16)visible_height);
-
- if (!copy) {
- return;
- }
-
- copy->x = (ta_uint16)(destination_x + column_start);
- copy->y = (ta_uint16)(destination_y + row_start);
-
- ta_uint32 *copy_pixels = (ta_uint32 *)copy->pixels;
-
- for (ta_uint32 row = 0; row < visible_height; row++) {
- const ta_uint32 *line = source_pixels + (ta_size)(row_start + (ta_int32)row) * source->width + column_start;
-
- for (ta_uint32 column = 0; column < visible_width; column++) {
- copy_pixels[(ta_size)row * visible_width + column] = line[column];
- }
- }
-
- if (!ta_display_sprite_add(copy)) {
- ta_free(copy);
- }
-
- return;
- }
-
- if (ta_display->buffers[1] == 0 || (ta_display->bpp != 32 && ta_display->bpp != 8)) {
- return;
- }
-
- for (ta_int32 row = row_start; row < row_end; row++) {
- for (ta_int32 column = column_start; column < column_end; column++) {
- ta_uint32 pixel = source_pixels[(ta_size)row * source->width + column];
- ta_uint32 alpha = pixel & 255;
-
- if (alpha != 0) {
- blit_pixel((ta_size)(destination_y + row) * ta_display->width + (ta_size)(destination_x + column), (pixel >> 24) & 255, (pixel >> 16) & 255, (pixel >> 8) & 255, alpha);
- }
- }
- }
-}
-
-void ta_text_destroy(ta_text *text) {
- if (!text) {
- return;
- }
-
- ta_free(text->sprite);
- text->sprite = 0;
- text->width = 0;
- text->height = 0;
-}
diff --git a/src/ta_title.c b/src/ta_title.c
@@ -1,3 +0,0 @@
-#include "ta_config.h"
-
-const char *ta_title = "Tux�nci v" TA_VERSION;
diff --git a/src/ta_vm.c b/src/ta_vm.c
@@ -1,623 +0,0 @@
-#include "ta_vm.h"
-#include "ta_abi.h"
-#include "ta_api.h"
-#include "ta_syscall.h"
-
-#define OP_LOAD 0x03
-#define OP_MISC_MEM 0x0F
-#define OP_OP_IMM 0x13
-#define OP_AUIPC 0x17
-#define OP_STORE 0x23
-#define OP_OP 0x33
-#define OP_LUI 0x37
-#define OP_BRANCH 0x63
-#define OP_JALR 0x67
-#define OP_JAL 0x6F
-#define OP_SYSTEM 0x73
-
-static ta_int32 sign_extend(ta_uint32 value, ta_uint32 bits) {
- ta_uint32 mask = (ta_uint32)1 << (bits - 1);
-
- return (ta_int32)((value ^ mask) - mask);
-}
-
-static ta_uint32 shift_right_arith(ta_uint32 value, ta_uint32 amount) {
- ta_uint32 result = value >> amount;
-
- if (amount != 0 && (value & 0x80000000u) != 0) {
- result |= ~(0xFFFFFFFFu >> amount);
- }
-
- return result;
-}
-
-static ta_uint32 mul_high_unsigned(ta_uint32 a, ta_uint32 b) {
- ta_uint32 a_lo = a & 0xFFFFu;
- ta_uint32 a_hi = a >> 16;
- ta_uint32 b_lo = b & 0xFFFFu;
- ta_uint32 b_hi = b >> 16;
- ta_uint32 lo_lo = a_lo * b_lo;
- ta_uint32 hi_lo = a_hi * b_lo;
- ta_uint32 lo_hi = a_lo * b_hi;
- ta_uint32 hi_hi = a_hi * b_hi;
- ta_uint32 cross = (lo_lo >> 16) + (hi_lo & 0xFFFFu) + lo_hi;
-
- return hi_hi + (hi_lo >> 16) + (cross >> 16);
-}
-
-static ta_uint32 mul_high_signed(ta_uint32 a, ta_uint32 b) {
- ta_uint32 result = mul_high_unsigned(a, b);
-
- if (a & 0x80000000u) {
- result -= b;
- }
-
- if (b & 0x80000000u) {
- result -= a;
- }
-
- return result;
-}
-
-static ta_uint32 mul_high_signed_unsigned(ta_uint32 a, ta_uint32 b) {
- ta_uint32 result = mul_high_unsigned(a, b);
-
- if (a & 0x80000000u) {
- result -= b;
- }
-
- return result;
-}
-
-static ta_uint32 div_signed(ta_uint32 a, ta_uint32 b) {
- if (b == 0) {
- return 0xFFFFFFFFu;
- }
-
- if (a == 0x80000000u && b == 0xFFFFFFFFu) {
- return a;
- }
-
- return (ta_uint32)((ta_int32)a / (ta_int32)b);
-}
-
-static ta_uint32 rem_signed(ta_uint32 a, ta_uint32 b) {
- if (b == 0) {
- return a;
- }
-
- if (a == 0x80000000u && b == 0xFFFFFFFFu) {
- return 0;
- }
-
- return (ta_uint32)((ta_int32)a % (ta_int32)b);
-}
-
-static ta_bool in_bounds(const ta_vm *vm, ta_uint32 addr, ta_uint32 size) {
- return size <= vm->mem_size && addr <= vm->mem_size - size;
-}
-
-ta_bool ta_vm_read8(const ta_vm *vm, ta_uint32 addr, ta_uint8 *out) {
- if (!in_bounds(vm, addr, 1)) {
- return ta_false;
- }
-
- *out = vm->mem[addr];
-
- return ta_true;
-}
-
-ta_bool ta_vm_read32(const ta_vm *vm, ta_uint32 addr, ta_uint32 *out) {
- if (!in_bounds(vm, addr, 4)) {
- return ta_false;
- }
-
- const ta_uint8 *p = vm->mem + addr;
-
- *out = (ta_uint32)p[0] | ((ta_uint32)p[1] << 8) | ((ta_uint32)p[2] << 16) | ((ta_uint32)p[3] << 24);
-
- return ta_true;
-}
-
-ta_bool ta_vm_write32(ta_vm *vm, ta_uint32 addr, ta_uint32 value) {
- if (!in_bounds(vm, addr, 4)) {
- return ta_false;
- }
-
- ta_uint8 *p = vm->mem + addr;
-
- p[0] = (ta_uint8)value;
- p[1] = (ta_uint8)(value >> 8);
- p[2] = (ta_uint8)(value >> 16);
- p[3] = (ta_uint8)(value >> 24);
-
- return ta_true;
-}
-
-static ta_bool load_value(const ta_vm *vm, ta_uint32 addr, ta_uint32 size, ta_uint32 *out) {
- if (!in_bounds(vm, addr, size)) {
- return ta_false;
- }
-
- const ta_uint8 *p = vm->mem + addr;
- ta_uint32 value = 0;
-
- for (ta_uint32 i = 0; i < size; i++) {
- value |= (ta_uint32)p[i] << (i * 8);
- }
-
- *out = value;
-
- return ta_true;
-}
-
-static ta_bool store_value(ta_vm *vm, ta_uint32 addr, ta_uint32 size, ta_uint32 value) {
- if (!in_bounds(vm, addr, size)) {
- return ta_false;
- }
-
- ta_uint8 *p = vm->mem + addr;
-
- for (ta_uint32 i = 0; i < size; i++) {
- p[i] = (ta_uint8)(value >> (i * 8));
- }
-
- return ta_true;
-}
-
-void *ta_vm_translate_pointer(const ta_vm *vm, ta_uint32 addr, ta_uint32 size) {
- if (size == 0 || !in_bounds(vm, addr, size)) {
- return TA_NULL;
- }
-
- return vm->mem + addr;
-}
-
-const char *ta_vm_cstr(const ta_vm *vm, ta_uint32 addr) {
- const char *string = (const char *)ta_vm_translate_pointer(vm, addr, 1);
-
- if (!string) {
- return TA_NULL;
- }
-
- for (ta_uint32 offset = addr; offset < vm->mem_size; offset++) {
- if (vm->mem[offset] == 0) {
- return string;
- }
- }
-
- return TA_NULL;
-}
-
-void ta_vm_init(ta_vm *vm, ta_uint8 *mem, ta_uint32 mem_size, ta_vm_ecall ecall, void *user) {
- vm->mem = mem;
- vm->mem_size = mem_size;
- vm->ecall = ecall;
- vm->user = user;
- ta_vm_reset(vm, 0, mem_size);
-}
-
-void ta_vm_reset(ta_vm *vm, ta_uint32 entry, ta_uint32 stack_top) {
- for (ta_uint32 i = 0; i < 32; i++) {
- vm->regs[i] = 0;
- }
-
- vm->regs[TA_VM_REG_SP] = stack_top & ~(ta_uint32)15;
- vm->pc = entry;
- vm->steps = 0;
-}
-
-ta_vm_status ta_vm_run(ta_vm *vm, ta_uint32 max_steps) {
- ta_uint32 *x = vm->regs;
- ta_uint32 pc = vm->pc;
- ta_uint32 steps = 0;
- ta_vm_status status = TA_VM_RUNNING;
-
- while (steps < max_steps) {
- ta_uint32 inst;
-
- if ((pc & 3) != 0) {
- status = TA_VM_FAULT_MISALIGNED;
- break;
- }
-
- if (!ta_vm_read32(vm, pc, &inst)) {
- status = TA_VM_FAULT_MEMORY;
- break;
- }
-
- ta_uint32 opcode = inst & 0x7F;
- ta_uint32 rd = (inst >> 7) & 31;
- ta_uint32 funct3 = (inst >> 12) & 7;
- ta_uint32 rs1 = (inst >> 15) & 31;
- ta_uint32 rs2 = (inst >> 20) & 31;
- ta_uint32 funct7 = inst >> 25;
- ta_uint32 a = x[rs1];
- ta_uint32 b = x[rs2];
- ta_uint32 next = pc + 4;
- ta_uint32 result = 0;
- ta_bool write = ta_false;
-
- switch (opcode) {
- case OP_LUI: {
- result = inst & 0xFFFFF000u;
- write = ta_true;
- break;
- }
- case OP_AUIPC: {
- result = pc + (inst & 0xFFFFF000u);
- write = ta_true;
- break;
- }
- case OP_JAL: {
- ta_uint32 imm = ((inst >> 31) << 20) | (((inst >> 12) & 0xFF) << 12) | (((inst >> 20) & 1) << 11) | (((inst >> 21) & 0x3FF) << 1);
-
- result = next;
- write = ta_true;
- next = pc + (ta_uint32)sign_extend(imm, 21);
- break;
- }
- case OP_JALR: {
- if (funct3 != 0) {
- status = TA_VM_FAULT_ILLEGAL;
- break;
- }
-
- result = next;
- write = ta_true;
- next = (a + (ta_uint32)sign_extend(inst >> 20, 12)) & ~(ta_uint32)1;
- break;
- }
- case OP_BRANCH: {
- ta_uint32 imm = ((inst >> 31) << 12) | (((inst >> 7) & 1) << 11) | (((inst >> 25) & 0x3F) << 5) | (((inst >> 8) & 0xF) << 1);
- ta_bool taken = ta_false;
-
- switch (funct3) {
- case 0: taken = a == b; break;
- case 1: taken = a != b; break;
- case 4: taken = (ta_int32)a < (ta_int32)b; break;
- case 5: taken = (ta_int32)a >= (ta_int32)b; break;
- case 6: taken = a < b; break;
- case 7: taken = a >= b; break;
- default: status = TA_VM_FAULT_ILLEGAL; break;
- }
-
- if (taken) {
- next = pc + (ta_uint32)sign_extend(imm, 13);
- }
-
- break;
- }
- case OP_LOAD: {
- ta_uint32 addr = a + (ta_uint32)sign_extend(inst >> 20, 12);
- ta_uint32 value = 0;
- ta_uint32 size = 0;
-
- switch (funct3) {
- case 0: case 4: size = 1; break;
- case 1: case 5: size = 2; break;
- case 2: size = 4; break;
- default: status = TA_VM_FAULT_ILLEGAL; break;
- }
-
- if (status != TA_VM_RUNNING) {
- break;
- }
-
- if (!load_value(vm, addr, size, &value)) {
- status = TA_VM_FAULT_MEMORY;
- break;
- }
-
- if (funct3 == 0) {
- value = (ta_uint32)sign_extend(value, 8);
- } else if (funct3 == 1) {
- value = (ta_uint32)sign_extend(value, 16);
- }
-
- result = value;
- write = ta_true;
- break;
- }
- case OP_STORE: {
- ta_uint32 imm = ((inst >> 25) << 5) | ((inst >> 7) & 31);
- ta_uint32 addr = a + (ta_uint32)sign_extend(imm, 12);
- ta_uint32 size = 0;
-
- switch (funct3) {
- case 0: size = 1; break;
- case 1: size = 2; break;
- case 2: size = 4; break;
- default: status = TA_VM_FAULT_ILLEGAL; break;
- }
-
- if (status != TA_VM_RUNNING) {
- break;
- }
-
- if (!store_value(vm, addr, size, b)) {
- status = TA_VM_FAULT_MEMORY;
- }
-
- break;
- }
- case OP_OP_IMM: {
- ta_uint32 imm = (ta_uint32)sign_extend(inst >> 20, 12);
- ta_uint32 shamt = rs2;
-
- switch (funct3) {
- case 0: result = a + imm; break;
- case 2: result = (ta_int32)a < (ta_int32)imm; break;
- case 3: result = a < imm; break;
- case 4: result = a ^ imm; break;
- case 6: result = a | imm; break;
- case 7: result = a & imm; break;
- case 1: {
- if (funct7 != 0x00) {
- status = TA_VM_FAULT_ILLEGAL;
- }
-
- result = a << shamt;
- break;
- }
- default: {
- if (funct7 == 0x00) {
- result = a >> shamt;
- } else if (funct7 == 0x20) {
- result = shift_right_arith(a, shamt);
- } else {
- status = TA_VM_FAULT_ILLEGAL;
- }
-
- break;
- }
- }
-
- write = ta_true;
- break;
- }
- case OP_OP: {
- if (funct7 == 0x01) {
- switch (funct3) {
- case 0: result = a * b; break;
- case 1: result = mul_high_signed(a, b); break;
- case 2: result = mul_high_signed_unsigned(a, b); break;
- case 3: result = mul_high_unsigned(a, b); break;
- case 4: result = div_signed(a, b); break;
- case 5: result = b == 0 ? 0xFFFFFFFFu : a / b; break;
- case 6: result = rem_signed(a, b); break;
- default: result = b == 0 ? a : a % b; break;
- }
- } else if (funct7 == 0x00 || funct7 == 0x20) {
- ta_bool alt = funct7 == 0x20;
-
- if (alt && funct3 != 0 && funct3 != 5) {
- status = TA_VM_FAULT_ILLEGAL;
- break;
- }
-
- switch (funct3) {
- case 0: result = alt ? a - b : a + b; break;
- case 1: result = a << (b & 31); break;
- case 2: result = (ta_int32)a < (ta_int32)b; break;
- case 3: result = a < b; break;
- case 4: result = a ^ b; break;
- case 5: result = alt ? shift_right_arith(a, b & 31) : a >> (b & 31); break;
- case 6: result = a | b; break;
- default: result = a & b; break;
- }
- } else {
- status = TA_VM_FAULT_ILLEGAL;
- break;
- }
-
- write = ta_true;
- break;
- }
- case OP_MISC_MEM: {
- break;
- }
- case OP_SYSTEM: {
- ta_uint32 imm = inst >> 20;
-
- if (funct3 != 0 || rd != 0 || rs1 != 0) {
- status = TA_VM_FAULT_ILLEGAL;
- } else if (imm == 0) {
- vm->pc = pc;
-
- if (vm->ecall) {
- status = vm->ecall(vm, vm->user);
- } else {
- status = TA_VM_FAULT_ILLEGAL;
- }
- } else if (imm == 1) {
- status = TA_VM_FAULT_EBREAK;
- } else {
- status = TA_VM_FAULT_ILLEGAL;
- }
-
- break;
- }
- default: {
- status = TA_VM_FAULT_ILLEGAL;
- break;
- }
- }
-
- if (status != TA_VM_RUNNING && status != TA_VM_HALTED) {
- break;
- }
-
- if (write && rd != 0) {
- x[rd] = result;
- }
-
- x[0] = 0;
- pc = next;
- steps++;
-
- if (status != TA_VM_RUNNING) {
- break;
- }
- }
-
- vm->pc = pc;
- vm->steps = steps;
-
- return status;
-}
-
-static ta_vm guest;
-static ta_uint8 *ram;
-static ta_vm_status guest_status;
-static ta_int32 exit_code;
-static ta_uint32 return_stub;
-static ta_uint32 call_result;
-static ta_bool ready;
-static ta_bool returned;
-ta_bool ta_vm_running;
-
-static ta_vm_status guest_ecall(ta_vm *machine, void *user) {
- (void)user;
-
- ta_uint32 *registers = machine->regs;
-
- switch (registers[TA_VM_REG_A7]) {
- case TA_SYS_EXIT: {
- exit_code = (ta_int32)registers[TA_VM_REG_A0];
-
- return TA_VM_HALTED;
- }
- case TA_SYS_READY: {
- return_stub = registers[TA_VM_REG_A0];
- ready = ta_true;
-
- return TA_VM_YIELDED;
- }
- case TA_SYS_RETURN: {
- call_result = registers[TA_VM_REG_A0];
- returned = ta_true;
-
- return TA_VM_YIELDED;
- }
- default: {
- return ta_syscall(machine);
- }
- }
-}
-
-static void finish_run(ta_bool completed) {
- if (completed) {
- guest_status = TA_VM_RUNNING;
- ta_vm_running = ta_true;
-
- return;
- }
-
- if (guest_status != TA_VM_HALTED) {
- exit_code = 1;
- }
-
- ta_vm_running = ta_false;
-}
-
-ta_bool ta_vm_load(const ta_uint8 *binary, ta_size size) {
- ta_vm_unload();
-
- if (!binary || size < 4 || size > TA_VM_RAM_SIZE) {
- return ta_false;
- }
-
- if (binary[0] != 0x54 || binary[1] != 0x41 ||
- binary[2] != 0x42 || binary[3] != 0x43) {
- return ta_false;
- }
-
- ram = (ta_uint8 *)ta_malloc(TA_VM_RAM_SIZE);
-
- if (!ram) {
- return ta_false;
- }
-
- for (ta_size i = 0; i < TA_VM_RAM_SIZE; i++) {
- ram[i] = i < size ? binary[i] : 0;
- }
-
- ta_vm_init(&guest, ram, TA_VM_RAM_SIZE, guest_ecall, TA_NULL);
- guest.pc = 4;
- guest_status = TA_VM_RUNNING;
- exit_code = 0;
- ready = ta_false;
- returned = ta_false;
- ta_vm_running = ta_true;
-
- guest_status = ta_vm_run(&guest, TA_VM_STEPS_PER_CALL);
-
- if (guest_status != TA_VM_YIELDED || !ready) {
- finish_run(ta_false);
- ta_vm_unload();
-
- return ta_false;
- }
-
- finish_run(ta_true);
-
- return ta_true;
-}
-
-ta_bool ta_vm_load_file(const char *path) {
- ta_fs_file file = ta_read_file(path);
-
- if (!file.data) {
- return ta_false;
- }
-
- ta_bool ok = ta_vm_load((const ta_uint8 *)file.data, file.size);
-
- ta_close_file(&file);
-
- return ok;
-}
-
-ta_bool ta_vm_call(ta_uint32 function, ta_uint32 arg0, ta_uint32 arg1, ta_uint32 arg2, ta_uint32 *result) {
- if (!ta_vm_running || !ready || function < 4 || (function & 3) != 0 || function >= TA_VM_RAM_SIZE) {
- return ta_false;
- }
-
- ta_vm_reset(&guest, function, TA_VM_RAM_SIZE);
- guest.regs[TA_VM_REG_RA] = return_stub;
- guest.regs[TA_VM_REG_A0] = arg0;
- guest.regs[TA_VM_REG_A1] = arg1;
- guest.regs[TA_VM_REG_A2] = arg2;
- returned = ta_false;
-
- guest_status = ta_vm_run(&guest, TA_VM_STEPS_PER_CALL);
-
- ta_bool completed = guest_status == TA_VM_YIELDED && returned;
-
- finish_run(completed);
-
- if (completed && result) {
- *result = call_result;
- }
-
- return completed;
-}
-
-ta_int32 ta_vm_exit_code(void) {
- return exit_code;
-}
-
-void ta_vm_unload(void) {
- ta_syscall_reset();
-
- if (ram) {
- ta_free(ram);
- ram = TA_NULL;
- }
-
- ready = ta_false;
- returned = ta_false;
- guest_status = TA_VM_HALTED;
- ta_vm_running = ta_false;
-}
diff --git a/src/text.c b/src/text.c
@@ -0,0 +1,1070 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "files.h"
+#include "display.h"
+#include "text.h"
+
+#define ONE 65536
+#define MAX_COORD 16384
+#define MAX_SIZE 2048
+#define MAX_SEGMENTS 64
+
+typedef struct {
+ int32_t x;
+ int32_t y;
+} ta_text_pt;
+
+typedef struct {
+ int32_t clip_x0;
+ int32_t clip_y0;
+ int32_t clip_x1;
+ int32_t clip_y1;
+ int32_t scale;
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ uint8_t border_thickness;
+ uint8_t border_red;
+ uint8_t border_green;
+ uint8_t border_blue;
+ uint32_t *sprite_pixels;
+ int32_t sprite_x;
+ int32_t sprite_y;
+ int32_t sprite_width;
+} ta_text_ctx;
+
+typedef struct {
+ const uint8_t *glyf;
+ const uint8_t *loca;
+ const uint8_t *hmtx;
+ const uint8_t *cmap;
+ uint32_t glyf_length;
+ uint32_t cmap_length;
+ uint32_t units_per_em;
+ uint32_t horizontal_metric_count;
+ uint32_t glyph_count;
+ bool long_loca;
+ int32_t ascent;
+ int32_t descent;
+ int32_t line_gap;
+} ta_text_font;
+
+static ta_text_font font;
+static int font_state;
+
+static uint32_t read_u16(const uint8_t *data) {
+ return ((uint32_t)data[0] << 8) | data[1];
+}
+
+static int32_t read_s16(const uint8_t *data) {
+ uint32_t value = read_u16(data);
+
+ if (value >= 0x8000) {
+ return (int32_t)value - 0x10000;
+ }
+
+ return (int32_t)value;
+}
+
+static uint32_t read_u32(const uint8_t *data) {
+ return (read_u16(data) << 16) | read_u16(data + 2);
+}
+
+static uint64_t unsigned_divide64(uint64_t numerator, uint32_t denominator) {
+#if TA_BITS == 64
+ return numerator / denominator;
+#else
+ if ((numerator >> 32) == 0) {
+ return (uint32_t)numerator / denominator;
+ }
+
+ uint64_t quotient = 0;
+ uint64_t remainder = 0;
+
+ for (int bit = 63; bit >= 0; bit--) {
+ remainder = (remainder << 1) | ((numerator >> bit) & 1);
+
+ if (remainder >= denominator) {
+ remainder -= denominator;
+ quotient |= (uint64_t)1 << bit;
+ }
+ }
+
+ return quotient;
+#endif
+}
+
+static int64_t signed_divide64(int64_t numerator, int32_t denominator) {
+ bool negative = false;
+ uint64_t unsigned_numerator;
+ uint32_t unsigned_denominator;
+
+ if (numerator < 0) {
+ negative = !negative;
+ unsigned_numerator = (uint64_t)(-numerator);
+ } else {
+ unsigned_numerator = (uint64_t)numerator;
+ }
+
+ if (denominator < 0) {
+ negative = !negative;
+ unsigned_denominator = (uint32_t)(-denominator);
+ } else {
+ unsigned_denominator = (uint32_t)denominator;
+ }
+
+ int64_t quotient = (int64_t)unsigned_divide64(unsigned_numerator, unsigned_denominator);
+
+ return negative ? -quotient : quotient;
+}
+
+static int32_t fixed_multiply(int32_t a, int32_t b) {
+ return (int32_t)(((int64_t)a * b) >> 16);
+}
+
+static const uint8_t *find_table(const uint8_t *data, uint32_t length, const char *tag, uint32_t *out_length) {
+ if (length < 12) {
+ return 0;
+ }
+
+ uint32_t table_count = read_u16(data + 4);
+
+ if (length < 12 + table_count * 16) {
+ return 0;
+ }
+
+ for (uint32_t i = 0; i < table_count; i++) {
+ const uint8_t *record = data + 12 + i * 16;
+
+ if (record[0] == (uint8_t)tag[0] && record[1] == (uint8_t)tag[1] && record[2] == (uint8_t)tag[2] && record[3] == (uint8_t)tag[3]) {
+ uint32_t offset = read_u32(record + 8);
+ uint32_t table_length = read_u32(record + 12);
+
+ if (offset > length || table_length > length - offset) {
+ return 0;
+ }
+
+ *out_length = table_length;
+
+ return data + offset;
+ }
+ }
+
+ return 0;
+}
+
+static bool font_load(void) {
+ const uint8_t *font_data = ta_file_dejavu_sans;
+ uint32_t font_length = ta_file_dejavu_sans_len;
+
+ uint32_t head_length;
+ uint32_t hhea_length;
+ uint32_t maxp_length;
+ uint32_t loca_length;
+ uint32_t hmtx_length;
+ uint32_t cmap_length;
+
+ const uint8_t *head = find_table(font_data, font_length, "head", &head_length);
+ const uint8_t *hhea = find_table(font_data, font_length, "hhea", &hhea_length);
+ const uint8_t *maxp = find_table(font_data, font_length, "maxp", &maxp_length);
+ const uint8_t *cmap = find_table(font_data, font_length, "cmap", &cmap_length);
+
+ font.loca = find_table(font_data, font_length, "loca", &loca_length);
+ font.hmtx = find_table(font_data, font_length, "hmtx", &hmtx_length);
+ font.glyf = find_table(font_data, font_length, "glyf", &font.glyf_length);
+
+ if (!head || !hhea || !maxp || !font.loca || !font.hmtx || !font.glyf || !cmap) {
+ return false;
+ }
+
+ if (head_length < 54 || hhea_length < 36 || maxp_length < 6 || cmap_length < 4) {
+ return false;
+ }
+
+ font.units_per_em = read_u16(head + 18);
+ font.long_loca = read_s16(head + 50) != 0;
+ font.ascent = read_s16(hhea + 4);
+ font.descent = read_s16(hhea + 6);
+ font.line_gap = read_s16(hhea + 8);
+ font.horizontal_metric_count = read_u16(hhea + 34);
+ font.glyph_count = read_u16(maxp + 4);
+
+ if (font.units_per_em == 0 || font.horizontal_metric_count == 0 || font.glyph_count == 0) {
+ return false;
+ }
+
+ if (font.ascent - font.descent + font.line_gap <= 0) {
+ return false;
+ }
+
+ if (loca_length < (font.glyph_count + 1) * (font.long_loca ? 4u : 2u)) {
+ return false;
+ }
+
+ if (hmtx_length < font.horizontal_metric_count * 4) {
+ return false;
+ }
+
+ const uint8_t *best_subtable = 0;
+ uint32_t best_length = 0;
+ uint32_t subtable_count = read_u16(cmap + 2);
+
+ if (cmap_length < 4 + subtable_count * 8) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < subtable_count; i++) {
+ const uint8_t *record = cmap + 4 + i * 8;
+ uint32_t platform = read_u16(record);
+ uint32_t encoding = read_u16(record + 2);
+ uint32_t offset = read_u32(record + 4);
+
+ if (offset > cmap_length || cmap_length - offset < 16) {
+ continue;
+ }
+
+ if (read_u16(cmap + offset) != 4) {
+ continue;
+ }
+
+ if (platform == 3 && encoding == 1) {
+ best_subtable = cmap + offset;
+ best_length = cmap_length - offset;
+
+ break;
+ }
+
+ if (platform == 0 && !best_subtable) {
+ best_subtable = cmap + offset;
+ best_length = cmap_length - offset;
+ }
+ }
+
+ if (!best_subtable) {
+ return false;
+ }
+
+ uint32_t segment_count_x2 = read_u16(best_subtable + 6);
+
+ if (best_length < 16 + segment_count_x2 * 4) {
+ return false;
+ }
+
+ font.cmap = best_subtable;
+ font.cmap_length = best_length;
+
+ return true;
+}
+
+static bool font_ready(void) {
+ if (font_state == 0) {
+ font_state = font_load() ? 1 : -1;
+ }
+
+ return font_state == 1;
+}
+
+static uint32_t glyph_for(uint32_t character) {
+ if (character > 0xFFFF) {
+ return 0;
+ }
+
+ const uint8_t *table = font.cmap;
+ uint32_t segment_count_x2 = read_u16(table + 6);
+ const uint8_t *end_codes = table + 14;
+ const uint8_t *start_codes = table + 16 + segment_count_x2;
+ const uint8_t *deltas = table + 16 + segment_count_x2 * 2;
+ const uint8_t *range_offsets = table + 16 + segment_count_x2 * 3;
+
+ for (uint32_t i = 0; i < segment_count_x2 / 2; i++) {
+ if (character > read_u16(end_codes + i * 2)) {
+ continue;
+ }
+
+ uint32_t start = read_u16(start_codes + i * 2);
+
+ if (character < start) {
+ return 0;
+ }
+
+ uint32_t delta = read_u16(deltas + i * 2);
+ uint32_t range_offset = read_u16(range_offsets + i * 2);
+
+ if (range_offset == 0) {
+ return (character + delta) & 0xFFFF;
+ }
+
+ const uint8_t *entry = range_offsets + i * 2 + range_offset + (character - start) * 2;
+
+ if ((size_t)(entry + 2 - font.cmap) > font.cmap_length) {
+ return 0;
+ }
+
+ uint32_t glyph = read_u16(entry);
+
+ return glyph ? (glyph + delta) & 0xFFFF : 0;
+ }
+
+ return 0;
+}
+
+static uint32_t advance_of(uint32_t glyph_id) {
+ if (glyph_id >= font.horizontal_metric_count) {
+ glyph_id = font.horizontal_metric_count - 1;
+ }
+
+ return read_u16(font.hmtx + glyph_id * 4);
+}
+
+static const uint8_t *glyph_data(uint32_t glyph_id, uint32_t *length) {
+ if (glyph_id >= font.glyph_count) {
+ return 0;
+ }
+
+ uint32_t begin;
+ uint32_t end;
+
+ if (font.long_loca) {
+ begin = read_u32(font.loca + glyph_id * 4);
+ end = read_u32(font.loca + glyph_id * 4 + 4);
+ } else {
+ begin = read_u16(font.loca + glyph_id * 2) * 2;
+ end = read_u16(font.loca + glyph_id * 2 + 2) * 2;
+ }
+
+ if (end <= begin || end > font.glyf_length || end - begin < 10) {
+ return 0;
+ }
+
+ *length = end - begin;
+
+ return font.glyf + begin;
+}
+
+static void raster_line(int32_t *coverage, int32_t stride, int32_t width, int32_t height, int32_t start_x, int32_t start_y, int32_t end_x, int32_t end_y) {
+ if (start_y == end_y) {
+ return;
+ }
+
+ int32_t direction = 1;
+
+ if (start_y > end_y) {
+ int32_t temp;
+
+ direction = -1;
+
+ temp = start_x;
+ start_x = end_x;
+ end_x = temp;
+
+ temp = start_y;
+ start_y = end_y;
+ end_y = temp;
+ }
+
+ if (end_y <= 0 || start_y >= height * ONE) {
+ return;
+ }
+
+ int32_t delta_x = end_x - start_x;
+ int32_t delta_y_total = end_y - start_y;
+ int32_t first_row = start_y < 0 ? 0 : (start_y >> 16);
+ int32_t last_row = (end_y + (ONE - 1)) >> 16;
+
+ if (last_row > height) {
+ last_row = height;
+ }
+
+ int32_t current_x = start_x;
+
+ if (start_y < 0) {
+ current_x = start_x + (int32_t)signed_divide64((int64_t)delta_x * (0 - start_y), delta_y_total);
+ }
+
+ int32_t max_x = width * ONE;
+
+ for (int32_t row_index = first_row; row_index < last_row; row_index++) {
+ int32_t row_top = row_index * ONE;
+ int32_t row_bottom = row_top + ONE;
+ int32_t segment_top = start_y > row_top ? start_y : row_top;
+ int32_t segment_bottom = end_y < row_bottom ? end_y : row_bottom;
+ int32_t segment_height = segment_bottom - segment_top;
+ int32_t next_x;
+
+ if (segment_bottom == end_y) {
+ next_x = end_x;
+ } else {
+ next_x = start_x + (int32_t)signed_divide64((int64_t)delta_x * (segment_bottom - start_y), delta_y_total);
+ }
+
+ int32_t signed_height = segment_height * direction;
+ int32_t left_x = current_x < next_x ? current_x : next_x;
+ int32_t right_x = current_x < next_x ? next_x : current_x;
+
+ if (left_x < 0) {
+ left_x = 0;
+ }
+
+ if (right_x < 0) {
+ right_x = 0;
+ }
+
+ if (left_x > max_x) {
+ left_x = max_x;
+ }
+
+ if (right_x > max_x) {
+ right_x = max_x;
+ }
+
+ int32_t first_cell = left_x >> 16;
+ int32_t last_cell = (right_x + (ONE - 1)) >> 16;
+ int32_t *row = coverage + row_index * stride;
+
+ if (last_cell <= first_cell + 1) {
+ int32_t middle_offset = ((left_x + right_x) >> 1) - (first_cell << 16);
+
+ row[first_cell] += signed_height - fixed_multiply(signed_height, middle_offset);
+ row[first_cell + 1] += fixed_multiply(signed_height, middle_offset);
+ } else {
+ int32_t span = right_x - left_x;
+ int32_t first_fraction = left_x - (first_cell << 16);
+ int32_t first_remaining = ONE - first_fraction;
+ int32_t last_fraction = right_x - ((last_cell - 1) << 16);
+ int32_t first_area = (int32_t)unsigned_divide64((uint64_t)first_remaining * (uint64_t)first_remaining, (uint32_t)span * 2);
+ int32_t last_area = (int32_t)unsigned_divide64((uint64_t)last_fraction * (uint64_t)last_fraction, (uint32_t)span * 2);
+
+ row[first_cell] += fixed_multiply(signed_height, first_area);
+
+ if (last_cell == first_cell + 2) {
+ row[first_cell + 1] += fixed_multiply(signed_height, ONE - first_area - last_area);
+ } else {
+ int32_t slope_step = (int32_t)unsigned_divide64((uint64_t)ONE << 16, (uint32_t)span);
+ int32_t second_area = (int32_t)unsigned_divide64((uint64_t)(ONE + ONE / 2 - first_fraction) << 16, (uint32_t)span);
+
+ row[first_cell + 1] += fixed_multiply(signed_height, second_area - first_area);
+
+ for (int32_t cell = first_cell + 2; cell < last_cell - 1; cell++) {
+ row[cell] += fixed_multiply(signed_height, slope_step);
+ }
+
+ int32_t final_area = second_area + (last_cell - first_cell - 3) * slope_step;
+
+ row[last_cell - 1] += fixed_multiply(signed_height, ONE - final_area - last_area);
+ }
+
+ row[last_cell] += fixed_multiply(signed_height, last_area);
+ }
+
+ current_x = next_x;
+ }
+}
+
+static void raster_quad(int32_t *coverage, int32_t stride, int32_t width, int32_t height, ta_text_pt start, ta_text_pt control, ta_text_pt end) {
+ int32_t second_diff_x = start.x - 2 * control.x + end.x;
+ int32_t second_diff_y = start.y - 2 * control.y + end.y;
+
+ if (second_diff_x < 0) {
+ second_diff_x = -second_diff_x;
+ }
+
+ if (second_diff_y < 0) {
+ second_diff_y = -second_diff_y;
+ }
+
+ int32_t deviation = (second_diff_x + second_diff_y) >> 2;
+ int32_t segment_count = 1;
+
+ while (segment_count < MAX_SEGMENTS && deviation > (ONE / 16) * segment_count * segment_count) {
+ segment_count++;
+ }
+
+ ta_text_pt previous = start;
+
+ for (int32_t i = 1; i <= segment_count; i++) {
+ ta_text_pt current;
+
+ if (i == segment_count) {
+ current = end;
+ } else {
+ int32_t t = (int32_t)((uint32_t)i * ONE / (uint32_t)segment_count);
+ int32_t one_minus_t = ONE - t;
+ int64_t weight_start = (int64_t)one_minus_t * one_minus_t;
+ int64_t weight_ctrl = (int64_t)2 * one_minus_t * t;
+ int64_t weight_end = (int64_t)t * t;
+
+ current.x = (int32_t)((weight_start * start.x + weight_ctrl * control.x + weight_end * end.x) >> 32);
+ current.y = (int32_t)((weight_start * start.y + weight_ctrl * control.y + weight_end * end.y) >> 32);
+ }
+
+ raster_line(coverage, stride, width, height, previous.x, previous.y, current.x, current.y);
+ previous = current;
+ }
+}
+
+#define DIV255(t) ((((t) + 128) + (((t) + 128) >> 8)) >> 8)
+
+static uint32_t blend_channel(uint32_t destination, uint32_t source, uint32_t alpha) {
+ uint32_t total = destination * (255 - alpha) + source * alpha;
+
+ return DIV255(total);
+}
+
+static void put_pixel(const ta_text_ctx *ctx, int32_t pixel_x, int32_t pixel_y, uint32_t alpha) {
+ size_t index = (size_t)(pixel_y - ctx->sprite_y) * (size_t)ctx->sprite_width + (size_t)(pixel_x - ctx->sprite_x);
+ uint32_t destination = ctx->sprite_pixels[index];
+ uint32_t destination_alpha = destination & 255;
+ uint32_t inverse_alpha = 255 - alpha;
+ uint32_t output_alpha = alpha + (destination_alpha * inverse_alpha + 127) / 255;
+
+ if (output_alpha == 0) {
+ return;
+ }
+
+ uint32_t destination_red = (destination >> 24) & 255;
+ uint32_t destination_green = (destination >> 16) & 255;
+ uint32_t destination_blue = (destination >> 8) & 255;
+ uint32_t output_red = ((uint32_t)ctx->red * alpha * 255 + destination_red * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
+ uint32_t output_green = ((uint32_t)ctx->green * alpha * 255 + destination_green * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
+ uint32_t output_blue = ((uint32_t)ctx->blue * alpha * 255 + destination_blue * destination_alpha * inverse_alpha + output_alpha * 127) / (output_alpha * 255);
+
+ ctx->sprite_pixels[index] = (output_red << 24) | (output_green << 16) | (output_blue << 8) | output_alpha;
+}
+
+static void blit_pixel(size_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t alpha) {
+ if (ta_display->bpp == 32) {
+ uint32_t *pixel = (uint32_t *)ta_display->buffers[1] + index;
+ uint32_t destination = *pixel;
+
+ *pixel = (destination & 0xFF000000u) | (blend_channel((destination >> 16) & 255, red, alpha) << 16) | (blend_channel((destination >> 8) & 255, green, alpha) << 8) | blend_channel(destination & 255, blue, alpha);
+ } else {
+ uint8_t *pixel = (uint8_t *)ta_display->buffers[1] + index;
+ uint32_t destination = *pixel;
+ uint32_t dest_red = (destination >> 5) & 7;
+ uint32_t dest_green = (destination >> 2) & 7;
+ uint32_t dest_blue = destination & 3;
+
+ dest_red = (dest_red << 5) | (dest_red << 2) | (dest_red >> 1);
+ dest_green = (dest_green << 5) | (dest_green << 2) | (dest_green >> 1);
+ dest_blue = dest_blue * 85;
+
+ red = blend_channel(dest_red, red, alpha);
+ green = blend_channel(dest_green, green, alpha);
+ blue = blend_channel(dest_blue, blue, alpha);
+
+ *pixel = (uint8_t)(((red >> 5) << 5) | ((green >> 5) << 2) | (blue >> 6));
+ }
+}
+
+static void emit_pixel(const ta_text_ctx *ctx, int32_t pixel_x, int32_t pixel_y, uint32_t alpha) {
+ put_pixel(ctx, pixel_x, pixel_y, alpha);
+}
+
+static ta_text_pt transform_point(int32_t font_x, int32_t font_y, int32_t origin_x, int32_t origin_y, int32_t scale) {
+ ta_text_pt point;
+
+ point.x = origin_x + (int32_t)((int64_t)font_x * scale);
+ point.y = origin_y - (int32_t)((int64_t)font_y * scale);
+
+ return point;
+}
+
+static void draw_glyph(const ta_text_ctx *ctx, uint32_t glyph_id, int32_t pen_x, int32_t baseline_y) {
+ uint32_t glyph_length;
+ const uint8_t *glyph = glyph_data(glyph_id, &glyph_length);
+
+ if (!glyph) {
+ return;
+ }
+
+ int32_t contour_count = read_s16(glyph);
+
+ if (contour_count <= 0) {
+ return;
+ }
+
+ int32_t scale = ctx->scale;
+ int32_t glyph_left = pen_x + (int32_t)((int64_t)read_s16(glyph + 2) * scale);
+ int32_t glyph_right = pen_x + (int32_t)((int64_t)read_s16(glyph + 6) * scale);
+ int32_t glyph_top = baseline_y - (int32_t)((int64_t)read_s16(glyph + 8) * scale);
+ int32_t glyph_bottom = baseline_y - (int32_t)((int64_t)read_s16(glyph + 4) * scale);
+ int32_t region_x0 = glyph_left >> 16;
+ int32_t region_x1 = (glyph_right + (ONE - 1)) >> 16;
+ int32_t region_y0 = glyph_top >> 16;
+ int32_t region_y1 = (glyph_bottom + (ONE - 1)) >> 16;
+
+ if (region_x0 < ctx->clip_x0) {
+ region_x0 = ctx->clip_x0;
+ }
+
+ if (region_y0 < ctx->clip_y0) {
+ region_y0 = ctx->clip_y0;
+ }
+
+ if (region_x1 > ctx->clip_x1) {
+ region_x1 = ctx->clip_x1;
+ }
+
+ if (region_y1 > ctx->clip_y1) {
+ region_y1 = ctx->clip_y1;
+ }
+
+ if (region_x0 >= region_x1 || region_y0 >= region_y1) {
+ return;
+ }
+
+ int32_t region_width = region_x1 - region_x0;
+ int32_t region_height = region_y1 - region_y0;
+ int32_t stride = region_width + 2;
+ size_t coverage_bytes = (size_t)stride * (size_t)region_height * sizeof(int32_t);
+ const uint8_t *end = glyph + glyph_length;
+ const uint8_t *cursor = glyph + 10;
+
+ if ((size_t)(end - cursor) < (size_t)contour_count * 2 + 2) {
+ return;
+ }
+
+ uint32_t point_count = read_u16(cursor + (contour_count - 1) * 2) + 1;
+ const uint8_t *contour_ends = cursor;
+
+ cursor += contour_count * 2;
+ cursor += 2 + read_u16(cursor);
+
+ if (cursor > end || point_count > 4096) {
+ return;
+ }
+
+ size_t points_bytes = (size_t)point_count * 5;
+ void *memory = malloc(points_bytes + coverage_bytes + 8);
+
+ if (!memory) {
+ return;
+ }
+
+ int16_t *point_x = (int16_t *)memory;
+ int16_t *point_y = point_x + point_count;
+ uint8_t *flags = (uint8_t *)(point_y + point_count);
+ int32_t *coverage = (int32_t *)(((size_t)(flags + point_count) + 7) & ~(size_t)7);
+ bool parse_ok = true;
+
+ for (uint32_t i = 0; i < point_count && parse_ok;) {
+ if (cursor >= end) {
+ parse_ok = false;
+
+ break;
+ }
+
+ uint8_t flag = *cursor++;
+
+ flags[i++] = flag;
+
+ if (flag & 8) {
+ if (cursor >= end) {
+ parse_ok = false;
+
+ break;
+ }
+
+ uint32_t repeat_count = *cursor++;
+
+ while (repeat_count > 0 && i < point_count) {
+ flags[i++] = flag;
+ repeat_count--;
+ }
+ }
+ }
+
+ for (uint32_t pass = 0; pass < 2 && parse_ok; pass++) {
+ int16_t *output = pass ? point_y : point_x;
+ uint8_t short_bit = pass ? 4 : 2;
+ uint8_t same_bit = pass ? 32 : 16;
+ int32_t value = 0;
+
+ for (uint32_t i = 0; i < point_count; i++) {
+ uint8_t flag = flags[i];
+
+ if (flag & short_bit) {
+ if (cursor >= end) {
+ parse_ok = false;
+
+ break;
+ }
+
+ int32_t delta = *cursor++;
+
+ value += (flag & same_bit) ? delta : -delta;
+ } else if (!(flag & same_bit)) {
+ if (end - cursor < 2) {
+ parse_ok = false;
+
+ break;
+ }
+
+ value += read_s16(cursor);
+ cursor += 2;
+ }
+
+ output[i] = (int16_t)value;
+ }
+ }
+
+ if (!parse_ok) {
+ free(memory);
+
+ return;
+ }
+
+ for (size_t i = 0; i < coverage_bytes / sizeof(int32_t); i++) {
+ coverage[i] = 0;
+ }
+
+ int32_t origin_x = pen_x - region_x0 * ONE;
+ int32_t origin_y = baseline_y - region_y0 * ONE;
+
+#define POINT(index) transform_point((int32_t)point_x[index], (int32_t)point_y[index], origin_x, origin_y, scale)
+
+ uint32_t contour_first = 0;
+
+ for (int32_t contour = 0; contour < contour_count; contour++) {
+ uint32_t contour_last = read_u16(contour_ends + contour * 2);
+ uint32_t contour_start = contour_first;
+
+ contour_first = contour_last + 1;
+
+ if (contour_last < contour_start || contour_last >= point_count || contour_last - contour_start < 1) {
+ continue;
+ }
+
+ uint32_t contour_points = contour_last - contour_start + 1;
+
+ ta_text_pt start_point;
+ uint32_t begin;
+ uint32_t count;
+
+ if (flags[contour_start] & 1) {
+ start_point = POINT(contour_start);
+ begin = contour_start + 1;
+ count = contour_points - 1;
+ } else if (flags[contour_last] & 1) {
+ start_point = POINT(contour_last);
+ begin = contour_start;
+ count = contour_points - 1;
+ } else {
+ ta_text_pt first_point = POINT(contour_start);
+ ta_text_pt last_point = POINT(contour_last);
+ start_point.x = (first_point.x >> 1) + (last_point.x >> 1);
+ start_point.y = (first_point.y >> 1) + (last_point.y >> 1);
+ begin = contour_start;
+ count = contour_points;
+ }
+
+ ta_text_pt current = start_point;
+ ta_text_pt control = start_point;
+ bool has_control = false;
+
+ for (uint32_t k = 0; k < count; k++) {
+ uint32_t index = begin + k;
+ ta_text_pt point = POINT(index);
+
+ if (flags[index] & 1) {
+ if (has_control) {
+ raster_quad(coverage, stride, region_width, region_height, current, control, point);
+ } else {
+ raster_line(coverage, stride, region_width, region_height, current.x, current.y, point.x, point.y);
+ }
+
+ current = point;
+ has_control = false;
+ } else if (has_control) {
+ ta_text_pt midpoint;
+
+ midpoint.x = (control.x >> 1) + (point.x >> 1);
+ midpoint.y = (control.y >> 1) + (point.y >> 1);
+
+ raster_quad(coverage, stride, region_width, region_height, current, control, midpoint);
+
+ current = midpoint;
+ control = point;
+ } else {
+ control = point;
+ has_control = true;
+ }
+ }
+
+ if (has_control) {
+ raster_quad(coverage, stride, region_width, region_height, current, control, start_point);
+ } else {
+ raster_line(coverage, stride, region_width, region_height, current.x, current.y, start_point.x, start_point.y);
+ }
+ }
+
+#undef POINT
+
+ for (int32_t row_index = 0; row_index < region_height; row_index++) {
+ int32_t running_sum = 0;
+ const int32_t *row = coverage + row_index * stride;
+
+ for (int32_t column = 0; column < region_width; column++) {
+ running_sum += row[column];
+
+ int32_t amount = running_sum < 0 ? -running_sum : running_sum;
+
+ if (amount > ONE) {
+ amount = ONE;
+ }
+
+ uint32_t alpha = ((uint32_t)amount * 255 + 0x8000) >> 16;
+
+ if (alpha) {
+ emit_pixel(ctx, region_x0 + column, region_y0 + row_index, alpha);
+ }
+ }
+ }
+
+ free(memory);
+}
+
+static bool printable(uint32_t character) {
+ return character >= 32 && character != 127;
+}
+
+static uint32_t line_height_units(void) {
+ return (uint32_t)(font.ascent - font.descent + font.line_gap);
+}
+
+static void measure_units(const char *text, uint32_t *max_width, uint32_t *lines) {
+ uint32_t current_width = 0;
+ uint32_t best_width = 0;
+ uint32_t line_count = 1;
+
+ for (; *text; text++) {
+ uint32_t character = (uint8_t)*text;
+
+ if (character == '\n') {
+ if (current_width > best_width) {
+ best_width = current_width;
+ }
+
+ current_width = 0;
+ line_count++;
+ } else if (printable(character)) {
+ current_width += advance_of(glyph_for(character));
+ }
+ }
+
+ if (current_width > best_width) {
+ best_width = current_width;
+ }
+
+ *max_width = best_width;
+ *lines = line_count;
+}
+
+static void draw_text_pass(const char *text, int32_t x, int32_t y, const ta_text_ctx *ctx) {
+ int32_t scale = ctx->scale;
+ int32_t ascent = (int32_t)((int64_t)font.ascent * scale);
+ int32_t line_height = (int32_t)((int64_t)line_height_units() * scale);
+ int32_t pen_x = x * ONE;
+ int32_t baseline_y = y * ONE + ascent;
+
+ for (; *text; text++) {
+ uint32_t character = (uint8_t)*text;
+
+ if (character == '\n') {
+ pen_x = x * ONE;
+ baseline_y += line_height;
+
+ if (baseline_y - ascent >= ctx->clip_y1 * ONE) {
+ break;
+ }
+
+ continue;
+ }
+
+ if (!printable(character) || pen_x >= ctx->clip_x1 * ONE) {
+ continue;
+ }
+
+ uint32_t glyph_id = glyph_for(character);
+
+ draw_glyph(ctx, glyph_id, pen_x, baseline_y);
+ pen_x += (int32_t)((int64_t)advance_of(glyph_id) * scale);
+ }
+}
+
+static void draw_text_direct(const char *text, int32_t x, int32_t y, const ta_text_ctx *ctx) {
+ if (ctx->border_thickness != 0) {
+ ta_text_ctx border_ctx = *ctx;
+
+ border_ctx.red = ctx->border_red;
+ border_ctx.green = ctx->border_green;
+ border_ctx.blue = ctx->border_blue;
+ int32_t thickness = ctx->border_thickness;
+
+ for (int32_t offset_y = -thickness; offset_y <= thickness; offset_y++) {
+ for (int32_t offset_x = -thickness; offset_x <= thickness; offset_x++) {
+ if (offset_x == 0 && offset_y == 0) {
+ continue;
+ }
+
+ draw_text_pass(text, x + offset_x, y + offset_y, &border_ctx);
+ }
+ }
+ }
+
+ draw_text_pass(text, x, y, ctx);
+}
+
+void ta_text_rasterize(ta_text *text) {
+ if (!text) {
+ return;
+ }
+
+ ta_text_destroy(text);
+
+ if (!text->text || text->size == 0 || !font_ready()) {
+ return;
+ }
+
+ uint32_t units;
+ uint32_t lines;
+
+ measure_units(text->text, &units, &lines);
+
+ uint64_t scale = unsigned_divide64((uint64_t)text->size << 16, font.units_per_em);
+ uint64_t text_width = (units * scale + (ONE - 1)) >> 16;
+ uint64_t text_height = ((uint64_t)lines * line_height_units() * scale + (ONE - 1)) >> 16;
+ uint32_t border = text->border_width;
+ uint64_t sprite_width = text_width + (uint64_t)border * 2;
+ uint64_t sprite_height = text_height + (uint64_t)border * 2;
+
+ if (text_width == 0 || text_height == 0 || sprite_width > MAX_COORD || sprite_height > MAX_COORD) {
+ return;
+ }
+
+ size_t pixel_count = (size_t)sprite_width * (size_t)sprite_height;
+ ta_display_sprite *sprite = ta_display_sprite_create((uint16_t)sprite_width, (uint16_t)sprite_height);
+
+ if (!sprite) {
+ return;
+ }
+
+ uint32_t *pixels = (uint32_t *)sprite->pixels;
+
+ for (size_t i = 0; i < pixel_count; i++) {
+ pixels[i] = 0;
+ }
+
+ ta_text_ctx ctx;
+
+ ctx.scale = (int32_t)scale;
+ ctx.clip_x0 = 0;
+ ctx.clip_y0 = 0;
+ ctx.clip_x1 = (int32_t)sprite_width;
+ ctx.clip_y1 = (int32_t)sprite_height;
+ ctx.red = text->red;
+ ctx.green = text->green;
+ ctx.blue = text->blue;
+ ctx.border_thickness = text->border_width;
+ ctx.border_red = text->border_red;
+ ctx.border_green = text->border_green;
+ ctx.border_blue = text->border_blue;
+ ctx.sprite_pixels = pixels;
+ ctx.sprite_x = 0;
+ ctx.sprite_y = 0;
+ ctx.sprite_width = (int32_t)sprite_width;
+
+ draw_text_direct(text->text, (int32_t)border, (int32_t)border, &ctx);
+
+ text->width = (uint32_t)text_width;
+ text->height = (uint32_t)text_height;
+ text->sprite = sprite;
+}
+
+void ta_text_draw(ta_text *text) {
+ if (!text || !text->sprite) {
+ return;
+ }
+
+ const ta_display_sprite *source = text->sprite;
+ int32_t destination_x = (int32_t)text->x - (int32_t)text->border_width;
+ int32_t destination_y = (int32_t)text->y - (int32_t)text->border_width;
+ int32_t column_start = destination_x < 0 ? -destination_x : 0;
+ int32_t row_start = destination_y < 0 ? -destination_y : 0;
+ int32_t column_end = (int32_t)source->width;
+ int32_t row_end = (int32_t)source->height;
+
+ if (column_end > (int32_t)ta_display->width - destination_x) {
+ column_end = (int32_t)ta_display->width - destination_x;
+ }
+
+ if (row_end > (int32_t)ta_display->height - destination_y) {
+ row_end = (int32_t)ta_display->height - destination_y;
+ }
+
+ if (column_start >= column_end || row_start >= row_end) {
+ return;
+ }
+
+ const uint32_t *source_pixels = (const uint32_t *)source->pixels;
+
+ if (ta_display->sprite_mode) {
+ if (!ta_display_sprites) {
+ return;
+ }
+
+ uint32_t visible_width = (uint32_t)(column_end - column_start);
+ uint32_t visible_height = (uint32_t)(row_end - row_start);
+ ta_display_sprite *copy = ta_display_sprite_create((uint16_t)visible_width, (uint16_t)visible_height);
+
+ if (!copy) {
+ return;
+ }
+
+ copy->x = (uint16_t)(destination_x + column_start);
+ copy->y = (uint16_t)(destination_y + row_start);
+
+ uint32_t *copy_pixels = (uint32_t *)copy->pixels;
+
+ for (uint32_t row = 0; row < visible_height; row++) {
+ const uint32_t *line = source_pixels + (size_t)(row_start + (int32_t)row) * source->width + column_start;
+
+ for (uint32_t column = 0; column < visible_width; column++) {
+ copy_pixels[(size_t)row * visible_width + column] = line[column];
+ }
+ }
+
+ if (!ta_display_sprite_add(copy)) {
+ free(copy);
+ }
+
+ return;
+ }
+
+ if (ta_display->buffers[1] == 0 || (ta_display->bpp != 32 && ta_display->bpp != 8)) {
+ return;
+ }
+
+ for (int32_t row = row_start; row < row_end; row++) {
+ for (int32_t column = column_start; column < column_end; column++) {
+ uint32_t pixel = source_pixels[(size_t)row * source->width + column];
+ uint32_t alpha = pixel & 255;
+
+ if (alpha != 0) {
+ blit_pixel((size_t)(destination_y + row) * ta_display->width + (size_t)(destination_x + column), (pixel >> 24) & 255, (pixel >> 16) & 255, (pixel >> 8) & 255, alpha);
+ }
+ }
+ }
+}
+
+void ta_text_destroy(ta_text *text) {
+ if (!text) {
+ return;
+ }
+
+ free(text->sprite);
+ text->sprite = 0;
+ text->width = 0;
+ text->height = 0;
+}
diff --git a/src/title.c b/src/title.c
@@ -0,0 +1,3 @@
+#include "config.h"
+
+const char *ta_title = "Tux�nci v" TA_VERSION;
diff --git a/src/vm.c b/src/vm.c
@@ -0,0 +1,625 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include "vm.h"
+#include "syscall.h"
+#include "abi.h"
+#include "api.h"
+
+#define OP_LOAD 0x03
+#define OP_MISC_MEM 0x0F
+#define OP_OP_IMM 0x13
+#define OP_AUIPC 0x17
+#define OP_STORE 0x23
+#define OP_OP 0x33
+#define OP_LUI 0x37
+#define OP_BRANCH 0x63
+#define OP_JALR 0x67
+#define OP_JAL 0x6F
+#define OP_SYSTEM 0x73
+
+static int32_t sign_extend(uint32_t value, uint32_t bits) {
+ uint32_t mask = (uint32_t)1 << (bits - 1);
+
+ return (int32_t)((value ^ mask) - mask);
+}
+
+static uint32_t shift_right_arith(uint32_t value, uint32_t amount) {
+ uint32_t result = value >> amount;
+
+ if (amount != 0 && (value & 0x80000000u) != 0) {
+ result |= ~(0xFFFFFFFFu >> amount);
+ }
+
+ return result;
+}
+
+static uint32_t mul_high_unsigned(uint32_t a, uint32_t b) {
+ uint32_t a_lo = a & 0xFFFFu;
+ uint32_t a_hi = a >> 16;
+ uint32_t b_lo = b & 0xFFFFu;
+ uint32_t b_hi = b >> 16;
+ uint32_t lo_lo = a_lo * b_lo;
+ uint32_t hi_lo = a_hi * b_lo;
+ uint32_t lo_hi = a_lo * b_hi;
+ uint32_t hi_hi = a_hi * b_hi;
+ uint32_t cross = (lo_lo >> 16) + (hi_lo & 0xFFFFu) + lo_hi;
+
+ return hi_hi + (hi_lo >> 16) + (cross >> 16);
+}
+
+static uint32_t mul_high_signed(uint32_t a, uint32_t b) {
+ uint32_t result = mul_high_unsigned(a, b);
+
+ if (a & 0x80000000u) {
+ result -= b;
+ }
+
+ if (b & 0x80000000u) {
+ result -= a;
+ }
+
+ return result;
+}
+
+static uint32_t mul_high_signed_unsigned(uint32_t a, uint32_t b) {
+ uint32_t result = mul_high_unsigned(a, b);
+
+ if (a & 0x80000000u) {
+ result -= b;
+ }
+
+ return result;
+}
+
+static uint32_t div_signed(uint32_t a, uint32_t b) {
+ if (b == 0) {
+ return 0xFFFFFFFFu;
+ }
+
+ if (a == 0x80000000u && b == 0xFFFFFFFFu) {
+ return a;
+ }
+
+ return (uint32_t)((int32_t)a / (int32_t)b);
+}
+
+static uint32_t rem_signed(uint32_t a, uint32_t b) {
+ if (b == 0) {
+ return a;
+ }
+
+ if (a == 0x80000000u && b == 0xFFFFFFFFu) {
+ return 0;
+ }
+
+ return (uint32_t)((int32_t)a % (int32_t)b);
+}
+
+static bool in_bounds(const ta_vm *vm, uint32_t addr, uint32_t size) {
+ return size <= vm->mem_size && addr <= vm->mem_size - size;
+}
+
+bool ta_vm_read8(const ta_vm *vm, uint32_t addr, uint8_t *out) {
+ if (!in_bounds(vm, addr, 1)) {
+ return false;
+ }
+
+ *out = vm->mem[addr];
+
+ return true;
+}
+
+bool ta_vm_read32(const ta_vm *vm, uint32_t addr, uint32_t *out) {
+ if (!in_bounds(vm, addr, 4)) {
+ return false;
+ }
+
+ const uint8_t *p = vm->mem + addr;
+
+ *out = (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
+
+ return true;
+}
+
+bool ta_vm_write32(ta_vm *vm, uint32_t addr, uint32_t value) {
+ if (!in_bounds(vm, addr, 4)) {
+ return false;
+ }
+
+ uint8_t *p = vm->mem + addr;
+
+ p[0] = (uint8_t)value;
+ p[1] = (uint8_t)(value >> 8);
+ p[2] = (uint8_t)(value >> 16);
+ p[3] = (uint8_t)(value >> 24);
+
+ return true;
+}
+
+static bool load_value(const ta_vm *vm, uint32_t addr, uint32_t size, uint32_t *out) {
+ if (!in_bounds(vm, addr, size)) {
+ return false;
+ }
+
+ const uint8_t *p = vm->mem + addr;
+ uint32_t value = 0;
+
+ for (uint32_t i = 0; i < size; i++) {
+ value |= (uint32_t)p[i] << (i * 8);
+ }
+
+ *out = value;
+
+ return true;
+}
+
+static bool store_value(ta_vm *vm, uint32_t addr, uint32_t size, uint32_t value) {
+ if (!in_bounds(vm, addr, size)) {
+ return false;
+ }
+
+ uint8_t *p = vm->mem + addr;
+
+ for (uint32_t i = 0; i < size; i++) {
+ p[i] = (uint8_t)(value >> (i * 8));
+ }
+
+ return true;
+}
+
+void *ta_vm_translate_pointer(const ta_vm *vm, uint32_t addr, uint32_t size) {
+ if (size == 0 || !in_bounds(vm, addr, size)) {
+ return NULL;
+ }
+
+ return vm->mem + addr;
+}
+
+const char *ta_vm_cstr(const ta_vm *vm, uint32_t addr) {
+ const char *string = (const char *)ta_vm_translate_pointer(vm, addr, 1);
+
+ if (!string) {
+ return NULL;
+ }
+
+ for (uint32_t offset = addr; offset < vm->mem_size; offset++) {
+ if (vm->mem[offset] == 0) {
+ return string;
+ }
+ }
+
+ return NULL;
+}
+
+void ta_vm_init(ta_vm *vm, uint8_t *mem, uint32_t mem_size, ta_vm_ecall ecall, void *user) {
+ vm->mem = mem;
+ vm->mem_size = mem_size;
+ vm->ecall = ecall;
+ vm->user = user;
+ ta_vm_reset(vm, 0, mem_size);
+}
+
+void ta_vm_reset(ta_vm *vm, uint32_t entry, uint32_t stack_top) {
+ for (uint32_t i = 0; i < 32; i++) {
+ vm->regs[i] = 0;
+ }
+
+ vm->regs[TA_VM_REG_SP] = stack_top & ~(uint32_t)15;
+ vm->pc = entry;
+ vm->steps = 0;
+}
+
+ta_vm_status ta_vm_run(ta_vm *vm, uint32_t max_steps) {
+ uint32_t *x = vm->regs;
+ uint32_t pc = vm->pc;
+ uint32_t steps = 0;
+ ta_vm_status status = TA_VM_RUNNING;
+
+ while (steps < max_steps) {
+ uint32_t inst;
+
+ if ((pc & 3) != 0) {
+ status = TA_VM_FAULT_MISALIGNED;
+ break;
+ }
+
+ if (!ta_vm_read32(vm, pc, &inst)) {
+ status = TA_VM_FAULT_MEMORY;
+ break;
+ }
+
+ uint32_t opcode = inst & 0x7F;
+ uint32_t rd = (inst >> 7) & 31;
+ uint32_t funct3 = (inst >> 12) & 7;
+ uint32_t rs1 = (inst >> 15) & 31;
+ uint32_t rs2 = (inst >> 20) & 31;
+ uint32_t funct7 = inst >> 25;
+ uint32_t a = x[rs1];
+ uint32_t b = x[rs2];
+ uint32_t next = pc + 4;
+ uint32_t result = 0;
+ bool write = false;
+
+ switch (opcode) {
+ case OP_LUI: {
+ result = inst & 0xFFFFF000u;
+ write = true;
+ break;
+ }
+ case OP_AUIPC: {
+ result = pc + (inst & 0xFFFFF000u);
+ write = true;
+ break;
+ }
+ case OP_JAL: {
+ uint32_t imm = ((inst >> 31) << 20) | (((inst >> 12) & 0xFF) << 12) | (((inst >> 20) & 1) << 11) | (((inst >> 21) & 0x3FF) << 1);
+
+ result = next;
+ write = true;
+ next = pc + (uint32_t)sign_extend(imm, 21);
+ break;
+ }
+ case OP_JALR: {
+ if (funct3 != 0) {
+ status = TA_VM_FAULT_ILLEGAL;
+ break;
+ }
+
+ result = next;
+ write = true;
+ next = (a + (uint32_t)sign_extend(inst >> 20, 12)) & ~(uint32_t)1;
+ break;
+ }
+ case OP_BRANCH: {
+ uint32_t imm = ((inst >> 31) << 12) | (((inst >> 7) & 1) << 11) | (((inst >> 25) & 0x3F) << 5) | (((inst >> 8) & 0xF) << 1);
+ bool taken = false;
+
+ switch (funct3) {
+ case 0: taken = a == b; break;
+ case 1: taken = a != b; break;
+ case 4: taken = (int32_t)a < (int32_t)b; break;
+ case 5: taken = (int32_t)a >= (int32_t)b; break;
+ case 6: taken = a < b; break;
+ case 7: taken = a >= b; break;
+ default: status = TA_VM_FAULT_ILLEGAL; break;
+ }
+
+ if (taken) {
+ next = pc + (uint32_t)sign_extend(imm, 13);
+ }
+
+ break;
+ }
+ case OP_LOAD: {
+ uint32_t addr = a + (uint32_t)sign_extend(inst >> 20, 12);
+ uint32_t value = 0;
+ uint32_t size = 0;
+
+ switch (funct3) {
+ case 0: case 4: size = 1; break;
+ case 1: case 5: size = 2; break;
+ case 2: size = 4; break;
+ default: status = TA_VM_FAULT_ILLEGAL; break;
+ }
+
+ if (status != TA_VM_RUNNING) {
+ break;
+ }
+
+ if (!load_value(vm, addr, size, &value)) {
+ status = TA_VM_FAULT_MEMORY;
+ break;
+ }
+
+ if (funct3 == 0) {
+ value = (uint32_t)sign_extend(value, 8);
+ } else if (funct3 == 1) {
+ value = (uint32_t)sign_extend(value, 16);
+ }
+
+ result = value;
+ write = true;
+ break;
+ }
+ case OP_STORE: {
+ uint32_t imm = ((inst >> 25) << 5) | ((inst >> 7) & 31);
+ uint32_t addr = a + (uint32_t)sign_extend(imm, 12);
+ uint32_t size = 0;
+
+ switch (funct3) {
+ case 0: size = 1; break;
+ case 1: size = 2; break;
+ case 2: size = 4; break;
+ default: status = TA_VM_FAULT_ILLEGAL; break;
+ }
+
+ if (status != TA_VM_RUNNING) {
+ break;
+ }
+
+ if (!store_value(vm, addr, size, b)) {
+ status = TA_VM_FAULT_MEMORY;
+ }
+
+ break;
+ }
+ case OP_OP_IMM: {
+ uint32_t imm = (uint32_t)sign_extend(inst >> 20, 12);
+ uint32_t shamt = rs2;
+
+ switch (funct3) {
+ case 0: result = a + imm; break;
+ case 2: result = (int32_t)a < (int32_t)imm; break;
+ case 3: result = a < imm; break;
+ case 4: result = a ^ imm; break;
+ case 6: result = a | imm; break;
+ case 7: result = a & imm; break;
+ case 1: {
+ if (funct7 != 0x00) {
+ status = TA_VM_FAULT_ILLEGAL;
+ }
+
+ result = a << shamt;
+ break;
+ }
+ default: {
+ if (funct7 == 0x00) {
+ result = a >> shamt;
+ } else if (funct7 == 0x20) {
+ result = shift_right_arith(a, shamt);
+ } else {
+ status = TA_VM_FAULT_ILLEGAL;
+ }
+
+ break;
+ }
+ }
+
+ write = true;
+ break;
+ }
+ case OP_OP: {
+ if (funct7 == 0x01) {
+ switch (funct3) {
+ case 0: result = a * b; break;
+ case 1: result = mul_high_signed(a, b); break;
+ case 2: result = mul_high_signed_unsigned(a, b); break;
+ case 3: result = mul_high_unsigned(a, b); break;
+ case 4: result = div_signed(a, b); break;
+ case 5: result = b == 0 ? 0xFFFFFFFFu : a / b; break;
+ case 6: result = rem_signed(a, b); break;
+ default: result = b == 0 ? a : a % b; break;
+ }
+ } else if (funct7 == 0x00 || funct7 == 0x20) {
+ bool alt = funct7 == 0x20;
+
+ if (alt && funct3 != 0 && funct3 != 5) {
+ status = TA_VM_FAULT_ILLEGAL;
+ break;
+ }
+
+ switch (funct3) {
+ case 0: result = alt ? a - b : a + b; break;
+ case 1: result = a << (b & 31); break;
+ case 2: result = (int32_t)a < (int32_t)b; break;
+ case 3: result = a < b; break;
+ case 4: result = a ^ b; break;
+ case 5: result = alt ? shift_right_arith(a, b & 31) : a >> (b & 31); break;
+ case 6: result = a | b; break;
+ default: result = a & b; break;
+ }
+ } else {
+ status = TA_VM_FAULT_ILLEGAL;
+ break;
+ }
+
+ write = true;
+ break;
+ }
+ case OP_MISC_MEM: {
+ break;
+ }
+ case OP_SYSTEM: {
+ uint32_t imm = inst >> 20;
+
+ if (funct3 != 0 || rd != 0 || rs1 != 0) {
+ status = TA_VM_FAULT_ILLEGAL;
+ } else if (imm == 0) {
+ vm->pc = pc;
+
+ if (vm->ecall) {
+ status = vm->ecall(vm, vm->user);
+ } else {
+ status = TA_VM_FAULT_ILLEGAL;
+ }
+ } else if (imm == 1) {
+ status = TA_VM_FAULT_EBREAK;
+ } else {
+ status = TA_VM_FAULT_ILLEGAL;
+ }
+
+ break;
+ }
+ default: {
+ status = TA_VM_FAULT_ILLEGAL;
+ break;
+ }
+ }
+
+ if (status != TA_VM_RUNNING && status != TA_VM_HALTED) {
+ break;
+ }
+
+ if (write && rd != 0) {
+ x[rd] = result;
+ }
+
+ x[0] = 0;
+ pc = next;
+ steps++;
+
+ if (status != TA_VM_RUNNING) {
+ break;
+ }
+ }
+
+ vm->pc = pc;
+ vm->steps = steps;
+
+ return status;
+}
+
+static ta_vm guest;
+static uint8_t *ram;
+static ta_vm_status guest_status;
+static int32_t exit_code;
+static uint32_t return_stub;
+static uint32_t call_result;
+static bool ready;
+static bool returned;
+bool ta_vm_running;
+
+static ta_vm_status guest_ecall(ta_vm *machine, void *user) {
+ (void)user;
+
+ uint32_t *registers = machine->regs;
+
+ switch (registers[TA_VM_REG_A7]) {
+ case TA_SYS_EXIT: {
+ exit_code = (int32_t)registers[TA_VM_REG_A0];
+
+ return TA_VM_HALTED;
+ }
+ case TA_SYS_READY: {
+ return_stub = registers[TA_VM_REG_A0];
+ ready = true;
+
+ return TA_VM_YIELDED;
+ }
+ case TA_SYS_RETURN: {
+ call_result = registers[TA_VM_REG_A0];
+ returned = true;
+
+ return TA_VM_YIELDED;
+ }
+ default: {
+ return ta_syscall(machine);
+ }
+ }
+}
+
+static void finish_run(bool completed) {
+ if (completed) {
+ guest_status = TA_VM_RUNNING;
+ ta_vm_running = true;
+
+ return;
+ }
+
+ if (guest_status != TA_VM_HALTED) {
+ exit_code = 1;
+ }
+
+ ta_vm_running = false;
+}
+
+bool ta_vm_load(const uint8_t *binary, size_t size) {
+ ta_vm_unload();
+
+ if (!binary || size < 4 || size > TA_VM_RAM_SIZE) {
+ return false;
+ }
+
+ if (binary[0] != 0x54 || binary[1] != 0x41 ||
+ binary[2] != 0x42 || binary[3] != 0x43) {
+ return false;
+ }
+
+ ram = (uint8_t *)malloc(TA_VM_RAM_SIZE);
+
+ if (!ram) {
+ return false;
+ }
+
+ for (size_t i = 0; i < TA_VM_RAM_SIZE; i++) {
+ ram[i] = i < size ? binary[i] : 0;
+ }
+
+ ta_vm_init(&guest, ram, TA_VM_RAM_SIZE, guest_ecall, NULL);
+ guest.pc = 4;
+ guest_status = TA_VM_RUNNING;
+ exit_code = 0;
+ ready = false;
+ returned = false;
+ ta_vm_running = true;
+
+ guest_status = ta_vm_run(&guest, TA_VM_STEPS_PER_CALL);
+
+ if (guest_status != TA_VM_YIELDED || !ready) {
+ finish_run(false);
+ ta_vm_unload();
+
+ return false;
+ }
+
+ finish_run(true);
+
+ return true;
+}
+
+bool ta_vm_load_file(const char *path) {
+ ta_fs_file file = ta_read_file(path);
+
+ if (!file.data) {
+ return false;
+ }
+
+ bool ok = ta_vm_load((const uint8_t *)file.data, file.size);
+
+ ta_close_file(&file);
+
+ return ok;
+}
+
+bool ta_vm_call(uint32_t function, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t *result) {
+ if (!ta_vm_running || !ready || function < 4 || (function & 3) != 0 || function >= TA_VM_RAM_SIZE) {
+ return false;
+ }
+
+ ta_vm_reset(&guest, function, TA_VM_RAM_SIZE);
+ guest.regs[TA_VM_REG_RA] = return_stub;
+ guest.regs[TA_VM_REG_A0] = arg0;
+ guest.regs[TA_VM_REG_A1] = arg1;
+ guest.regs[TA_VM_REG_A2] = arg2;
+ returned = false;
+
+ guest_status = ta_vm_run(&guest, TA_VM_STEPS_PER_CALL);
+
+ bool completed = guest_status == TA_VM_YIELDED && returned;
+
+ finish_run(completed);
+
+ if (completed && result) {
+ *result = call_result;
+ }
+
+ return completed;
+}
+
+int32_t ta_vm_exit_code(void) {
+ return exit_code;
+}
+
+void ta_vm_unload(void) {
+ ta_syscall_reset();
+
+ if (ram) {
+ free(ram);
+ ram = NULL;
+ }
+
+ ready = false;
+ returned = false;
+ guest_status = TA_VM_HALTED;
+ ta_vm_running = false;
+}