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:
Mengine.mk | 6+++---
Ainclude/abi.h | 26++++++++++++++++++++++++++
Ainclude/api.h | 29+++++++++++++++++++++++++++++
Rinclude/ta_config.h.in -> include/config.h.in | 0
Ainclude/display.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/fb.h | 14++++++++++++++
Ainclude/fixed.h | 9+++++++++
Ainclude/fs.h | 11+++++++++++
Ainclude/image.h | 13+++++++++++++
Ainclude/keyboard.h | 21+++++++++++++++++++++
Rinclude/ta_keys.h -> include/keys.h | 0
Rinclude/ta_main.h -> include/main.h | 0
Ainclude/scene.h | 17+++++++++++++++++
Ainclude/screen.h | 13+++++++++++++
Ainclude/shapes.h | 11+++++++++++
Ainclude/syscall.h | 9+++++++++
Dinclude/ta_abi.h | 22----------------------
Dinclude/ta_api.h | 36------------------------------------
Dinclude/ta_display.h | 45---------------------------------------------
Dinclude/ta_fb.h | 14--------------
Dinclude/ta_fs.h | 11-----------
Dinclude/ta_image.h | 12------------
Dinclude/ta_keyboard.h | 20--------------------
Dinclude/ta_scene.h | 13-------------
Dinclude/ta_screen.h | 12------------
Dinclude/ta_syscall.h | 10----------
Dinclude/ta_text.h | 22----------------------
Dinclude/ta_types.h | 40----------------------------------------
Dinclude/ta_vm.h | 65-----------------------------------------------------------------
Ainclude/text.h | 22++++++++++++++++++++++
Rinclude/ta_title.h -> include/title.h | 0
Ainclude/vm.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/display.c | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/fb.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/image.c | 698+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/keyboard.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main.c | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/scene.c | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/screen.c | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/shapes.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/syscall.c | 296+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/ta_display.c | 135-------------------------------------------------------------------------------
Dsrc/ta_fb.c | 80-------------------------------------------------------------------------------
Dsrc/ta_image.c | 694-------------------------------------------------------------------------------
Dsrc/ta_keyboard.c | 47-----------------------------------------------
Dsrc/ta_main.c | 42------------------------------------------
Dsrc/ta_scene.c | 173-------------------------------------------------------------------------------
Dsrc/ta_screen.c | 136-------------------------------------------------------------------------------
Dsrc/ta_syscall.c | 278-------------------------------------------------------------------------------
Dsrc/ta_text.c | 1067-------------------------------------------------------------------------------
Dsrc/ta_title.c | 3---
Dsrc/ta_vm.c | 623-------------------------------------------------------------------------------
Asrc/text.c | 1070+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/title.c | 3+++
Asrc/vm.c | 625+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 = &registers[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 = &registers[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; +}