tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE
commit 923ad7c80529690e659f8f2305519345a213c774
parent e9acd21cb6b5b86ae832f48a114eec3e4faec6fa
Author: Connor Thomson <connor@gnufault.org>
Date: Thu, 29 Oct 2026 18:16:42 -0700
Move game code to the tavm
Diffstat:
37 files changed, 1465 insertions(+), 698 deletions(-)
diff --git a/Makefile b/Makefile
@@ -68,7 +68,7 @@ MARCH := native
ENGINE_SRCS := $(shell find "$(ROOT)/src" -type f -name '*.c' | sort)
ASSETS := $(shell find "$(ROOT)/files" -type f -not -name 'LICENSE' | sort)
-HEADERS := $(wildcard $(ROOT)/include/*.h $(ROOT)/include/screens/*.h)
+HEADERS := $(wildcard $(ROOT)/include/*.h)
GENERATED_SRCS := $(patsubst $(ROOT)/files/%,$(OBJ)/%,$(addsuffix .c,$(basename $(ASSETS))))
ENGINE_SRC_OBJS := $(patsubst $(ROOT)/src/%.c,$(OBJ)/src/%.o,$(ENGINE_SRCS))
GENERATED_OBJS := $(GENERATED_SRCS:.c=.o)
diff --git a/README.md b/README.md
@@ -14,3 +14,15 @@ Tuxánci currently supports:
- Win32
- X11
- Xbox (2001)
+
+## Engine and game
+The engine (`src/`, `include/`) is platform independent C that owns text, display, framebuffer,
+images, keyboard, mods, screen management, the window title and the RV32IM virtual machine.
+
+The game (`game/`) is a flat `tuxanci2.tab` binary run inside that VM. It owns the button
+lists and every screen (loading, main menu). The engine only knows which screens exist and
+which one is current; it calls the game's init, update and destroy functions for the active screen.
+
+`include/ta_abi.h` and `include/ta_keys.h` are shared by both sides, and `sdk/tuxanci2.h`
+wraps the engine syscalls for the game. Build the game with `make tab`
+(`CROSS=riscv64-unknown-elf-` if you do not have a riscv32 toolchain).
diff --git a/game/Makefile b/game/Makefile
@@ -1,4 +1,5 @@
-TARGET := tuxanci2
-SRCS := src
+TARGET := tuxanci2
+SRCS := src
+INCLUDES := src
include ../sdk/sdk.mk
diff --git a/game/src/main.c b/game/src/main.c
@@ -1,9 +1,10 @@
#include "tuxanci2.h"
+#include "ta_screens.h"
+#include "screens/ta_screen_loading.h"
+#include "screens/ta_screen_main_menu.h"
void main(void) {
- while (1) {
- ta_yield();
- }
-
- ta_exit(0);
+ ta_screen_register(TA_SCREEN_LOADING, ta_screen_loading_init, ta_screen_loading_update, ta_screen_loading_destroy);
+ ta_screen_register(TA_SCREEN_MAIN_MENU, ta_screen_main_menu_init, ta_screen_main_menu_update, ta_screen_main_menu_destroy);
+ ta_screen_switch(TA_SCREEN_LOADING);
}
diff --git a/game/src/screens/ta_screen_loading.c b/game/src/screens/ta_screen_loading.c
@@ -0,0 +1,40 @@
+#include "screens/ta_screen_loading.h"
+#include "ta_screens.h"
+
+#define TA_LOADING_LOGO_SIZE TA_FIXED(0.3)
+
+void ta_screen_loading_init(void) {
+ ta_image_init(TA_ASSET_LOADING_LOGO);
+}
+
+void ta_screen_loading_update(void) {
+ ta_uint32 width;
+ ta_uint32 height;
+
+ if (!ta_image_get_size(TA_ASSET_LOADING_LOGO, &width, &height)) {
+ return;
+ }
+
+ ta_uint64 frame_width = ta_display_width();
+ ta_uint64 frame_height = ta_display_height();
+ ta_fixed draw_width;
+ ta_fixed draw_height;
+
+ if (frame_width * height <= frame_height * width) {
+ draw_width = TA_LOADING_LOGO_SIZE;
+ draw_height = (ta_fixed)((TA_LOADING_LOGO_SIZE * (ta_uint64)height * frame_width) / ((ta_uint64)width * frame_height));
+ } else {
+ draw_height = TA_LOADING_LOGO_SIZE;
+ draw_width = (ta_fixed)((TA_LOADING_LOGO_SIZE * (ta_uint64)width * frame_height) / ((ta_uint64)height * frame_width));
+ }
+
+ ta_fixed x = (TA_FIXED_ONE - draw_width) / 2;
+ ta_fixed y = (TA_FIXED_ONE - draw_height) / 2;
+
+ ta_image_draw(TA_ASSET_LOADING_LOGO, x, y, draw_width, draw_height, 0);
+
+ ta_screen_switch(TA_SCREEN_MAIN_MENU);
+}
+
+void ta_screen_loading_destroy(void) {
+}
diff --git a/game/src/screens/ta_screen_loading.h b/game/src/screens/ta_screen_loading.h
@@ -0,0 +1,10 @@
+#ifndef TA_SCREEN_LOADING_H
+#define TA_SCREEN_LOADING_H
+
+#include "tuxanci2.h"
+
+void ta_screen_loading_init(void);
+void ta_screen_loading_update(void);
+void ta_screen_loading_destroy(void);
+
+#endif // TA_SCREEN_LOADING_H
diff --git a/game/src/screens/ta_screen_main_menu.c b/game/src/screens/ta_screen_main_menu.c
@@ -0,0 +1,27 @@
+#include "screens/ta_screen_main_menu.h"
+#include "ta_button.h"
+
+static ta_button_lists menu;
+
+static void quit(void) {
+ ta_exit(0);
+}
+
+void ta_screen_main_menu_init(void) {
+ ta_image_init(TA_ASSET_BG);
+
+ ta_button_lists_init(&menu);
+ ta_button_lists_add(&menu, "Play", TA_NULL);
+ ta_button_lists_add(&menu, "Quit", quit);
+}
+
+void ta_screen_main_menu_update(void) {
+ ta_button_lists_update(&menu);
+
+ ta_image_draw(TA_ASSET_BG, 0, 0, TA_FIXED_ONE, TA_FIXED_ONE, 0);
+ ta_button_lists_draw(&menu);
+}
+
+void ta_screen_main_menu_destroy(void) {
+ ta_button_lists_destroy(&menu);
+}
diff --git a/game/src/screens/ta_screen_main_menu.h b/game/src/screens/ta_screen_main_menu.h
@@ -0,0 +1,10 @@
+#ifndef TA_SCREEN_MAIN_MENU_H
+#define TA_SCREEN_MAIN_MENU_H
+
+#include "tuxanci2.h"
+
+void ta_screen_main_menu_init(void);
+void ta_screen_main_menu_update(void);
+void ta_screen_main_menu_destroy(void);
+
+#endif // TA_SCREEN_MAIN_MENU_H
diff --git a/game/src/ta_button.c b/game/src/ta_button.c
@@ -0,0 +1,138 @@
+#include "ta_button.h"
+
+#define TA_BUTTON_FONT_SIZE 32
+#define TA_BUTTON_FONT_SIZE_MAX 255
+#define TA_BUTTON_SPACING 48
+#define TA_BUTTON_COLOR_IDLE 118
+#define TA_BUTTON_COLOR_SELECTED 255
+#define TA_BUTTON_REFERENCE_WIDTH 800
+#define TA_BUTTON_REFERENCE_HEIGHT 600
+
+static ta_uint32 scale_to_display(ta_uint32 value) {
+ ta_uint32 display_width = ta_display_width();
+ ta_uint32 display_height = ta_display_height();
+ ta_uint32 numerator = display_width;
+ ta_uint32 denominator = TA_BUTTON_REFERENCE_WIDTH;
+
+ if ((ta_uint64)display_width * TA_BUTTON_REFERENCE_HEIGHT >= (ta_uint64)display_height * TA_BUTTON_REFERENCE_WIDTH) {
+ numerator = display_height;
+ denominator = TA_BUTTON_REFERENCE_HEIGHT;
+ }
+
+ ta_uint32 scaled = (value * numerator * 2 + denominator) / (denominator * 2);
+
+ return scaled == 0 ? 1 : scaled;
+}
+
+void ta_button_lists_init(ta_button_lists *list) {
+ if (list == TA_NULL) {
+ return;
+ }
+
+ list->size = 0;
+ list->selected = 0;
+ list->up_was_down = ta_false;
+ list->down_was_down = ta_false;
+ list->enter_was_down = ta_false;
+}
+
+ta_bool ta_button_lists_add(ta_button_lists *list, const char *label, ta_button_callback callback) {
+ if (list == TA_NULL || label == TA_NULL || list->size >= TA_BUTTON_MAX) {
+ return ta_false;
+ }
+
+ ta_uint32 font_size = scale_to_display(TA_BUTTON_FONT_SIZE);
+
+ if (font_size > TA_BUTTON_FONT_SIZE_MAX) {
+ font_size = TA_BUTTON_FONT_SIZE_MAX;
+ }
+
+ ta_text_handle text_idle = ta_text_create(label, font_size, TA_RGB(TA_BUTTON_COLOR_IDLE, TA_BUTTON_COLOR_IDLE, TA_BUTTON_COLOR_IDLE), 1, TA_RGB(0, 0, 0));
+ ta_text_handle text_selected = ta_text_create(label, font_size, TA_RGB(TA_BUTTON_COLOR_SELECTED, TA_BUTTON_COLOR_SELECTED, TA_BUTTON_COLOR_SELECTED), 1, TA_RGB(0, 0, 0));
+
+ if (text_idle == 0 || text_selected == 0) {
+ ta_text_destroy(text_idle);
+ ta_text_destroy(text_selected);
+
+ return ta_false;
+ }
+
+ ta_button *button = &list->buttons[list->size];
+
+ button->label = label;
+ button->callback = callback;
+ button->text_idle = text_idle;
+ button->text_selected = text_selected;
+ button->text_width = ta_text_width(text_idle);
+ button->text_height = ta_text_height(text_idle);
+ list->size++;
+
+ return ta_true;
+}
+
+void ta_button_lists_update(ta_button_lists *list) {
+ if (list == TA_NULL || list->size == 0) {
+ return;
+ }
+
+ ta_bool up_down = ta_keyboard_is_down(KEY_UP);
+ ta_bool down_down = ta_keyboard_is_down(KEY_DOWN);
+ ta_bool enter_down = ta_keyboard_is_down(KEY_ENTER);
+ ta_bool up_pressed = up_down && !list->up_was_down;
+ ta_bool down_pressed = down_down && !list->down_was_down;
+ ta_bool enter_pressed = enter_down && !list->enter_was_down;
+
+ list->up_was_down = up_down;
+ list->down_was_down = down_down;
+ list->enter_was_down = enter_down;
+
+ if (down_pressed || up_pressed) {
+ if (down_pressed) {
+ list->selected = (list->selected + 1) % list->size;
+ } else {
+ list->selected = list->selected == 0 ? list->size - 1 : list->selected - 1;
+ }
+ }
+
+ if (enter_pressed && list->buttons[list->selected].callback != TA_NULL) {
+ list->buttons[list->selected].callback();
+ }
+}
+
+void ta_button_lists_draw(const ta_button_lists *list) {
+ if (list == TA_NULL || list->size == 0) {
+ return;
+ }
+
+ ta_uint32 display_width = ta_display_width();
+ ta_uint32 display_height = ta_display_height();
+ ta_int32 spacing = (ta_int32)scale_to_display(TA_BUTTON_SPACING);
+ ta_int32 center_y = (ta_int32)(display_height / 2);
+ ta_int32 total_span = (ta_int32)(list->size - 1) * spacing;
+ ta_int32 start_y = center_y - (total_span / 2);
+
+ for (ta_size i = 0; i < list->size; i++) {
+ const ta_button *button = &list->buttons[i];
+ ta_text_handle text = i == list->selected ? button->text_selected : button->text_idle;
+ ta_int32 y = start_y + (ta_int32)i * spacing - (ta_int32)(button->text_height / 2);
+
+ if (y < 0 || (ta_size)y >= display_height || button->text_width > display_width) {
+ continue;
+ }
+
+ ta_text_draw(text, (display_width - button->text_width) / 2, (ta_uint32)y);
+ }
+}
+
+void ta_button_lists_destroy(ta_button_lists *list) {
+ if (list == TA_NULL) {
+ return;
+ }
+
+ for (ta_size i = 0; i < list->size; i++) {
+ ta_text_destroy(list->buttons[i].text_idle);
+ ta_text_destroy(list->buttons[i].text_selected);
+ }
+
+ ta_button_lists_init(list);
+}
diff --git a/game/src/ta_button.h b/game/src/ta_button.h
@@ -0,0 +1,34 @@
+#ifndef TA_BUTTON_H
+#define TA_BUTTON_H
+
+#include "tuxanci2.h"
+
+#define TA_BUTTON_MAX 8
+
+typedef void (*ta_button_callback)(void);
+
+typedef struct {
+ const char *label;
+ ta_button_callback callback;
+ ta_text_handle text_idle;
+ ta_text_handle text_selected;
+ ta_uint32 text_width;
+ ta_uint32 text_height;
+} ta_button;
+
+typedef struct {
+ ta_button buttons[TA_BUTTON_MAX];
+ ta_size size;
+ ta_size selected;
+ ta_bool up_was_down;
+ ta_bool down_was_down;
+ ta_bool enter_was_down;
+} ta_button_lists;
+
+void ta_button_lists_init(ta_button_lists *list);
+ta_bool ta_button_lists_add(ta_button_lists *list, const char *label, ta_button_callback callback);
+void ta_button_lists_update(ta_button_lists *list);
+void ta_button_lists_draw(const ta_button_lists *list);
+void ta_button_lists_destroy(ta_button_lists *list);
+
+#endif // TA_BUTTON_H
diff --git a/game/src/ta_screens.h b/game/src/ta_screens.h
@@ -0,0 +1,9 @@
+#ifndef TA_SCREENS_H
+#define TA_SCREENS_H
+
+typedef enum {
+ TA_SCREEN_LOADING,
+ TA_SCREEN_MAIN_MENU
+} ta_screens;
+
+#endif // TA_SCREENS_H
diff --git a/include/screens/ta_screen_loading.h b/include/screens/ta_screen_loading.h
@@ -1,10 +0,0 @@
-#ifndef TA_SCREEN_LOADING_H
-#define TA_SCREEN_LOADING_H
-
-#include "ta_types.h"
-
-void ta_screen_loading_init(void);
-ta_bool ta_screen_loading_update(void);
-void ta_screen_loading_destroy(void);
-
-#endif // TA_SCREEN_LOADING_H
diff --git a/include/screens/ta_screen_main_menu.h b/include/screens/ta_screen_main_menu.h
@@ -1,10 +0,0 @@
-#ifndef TA_SCREEN_MAIN_MENU_H
-#define TA_SCREEN_MAIN_MENU_H
-
-#include "ta_types.h"
-
-void ta_screen_main_menu_init(void);
-ta_bool ta_screen_main_menu_update(void);
-void ta_screen_main_menu_destroy(void);
-
-#endif // TA_SCREEN_MAIN_MENU_H
diff --git a/include/ta_abi.h b/include/ta_abi.h
@@ -0,0 +1,25 @@
+#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
+
+#define TA_ASSET_BG 0
+#define TA_ASSET_LOADING_LOGO 1
+
+#endif // TA_ABI_H
diff --git a/include/ta_button.h b/include/ta_button.h
@@ -1,34 +0,0 @@
-#ifndef TA_BUTTON_H
-#define TA_BUTTON_H
-
-#include "ta_text.h"
-#include "ta_types.h"
-
-typedef void (*ta_button_callback)(void);
-
-typedef struct {
- const char *label;
- ta_button_callback callback;
- ta_text text_idle;
- ta_text text_selected;
-} ta_button;
-
-typedef struct {
- ta_button *buttons;
- ta_size size;
- ta_size selected;
- ta_size animation_from;
- ta_uint32 animation_frame;
- ta_bool animation_active;
- ta_bool up_was_down;
- ta_bool down_was_down;
- ta_bool enter_was_down;
-} ta_button_lists;
-
-void ta_button_lists_init(ta_button_lists *list);
-ta_bool ta_button_lists_add(ta_button_lists *list, const char *label, ta_button_callback callback);
-ta_bool ta_button_lists_update(ta_button_lists *list);
-void ta_button_lists_draw(const ta_button_lists *list);
-void ta_button_lists_destroy(ta_button_lists *list);
-
-#endif
diff --git a/include/ta_keyboard.h b/include/ta_keyboard.h
@@ -1,140 +1,11 @@
#ifndef TA_KEYBOARD_H
#define TA_KEYBOARD_H
+#include "ta_keys.h"
#include "ta_types.h"
#define TA_KEYBOARD_MAX_KEYS_DOWN 5
-typedef enum {
- KEY_NONE = 0,
- KEY_BACKSPACE = 8,
- KEY_TAB = 9,
- KEY_ENTER = 13,
- KEY_ESCAPE = 27,
- KEY_SPACE = 32,
- KEY_EXCLAMATION_MARK = 33,
- KEY_QUOTATION_MARK = 34,
- KEY_HASH = 35,
- KEY_DOLLAR_SIGN = 36,
- KEY_PERCENT_SIGN = 37,
- KEY_AMPERSAND = 38,
- KEY_APOSTROPHE = 39,
- KEY_LEFT_PARENTHESIS = 40,
- KEY_RIGHT_PARENTHESIS = 41,
- KEY_ASTERISK = 42,
- KEY_PLUS = 43,
- KEY_COMMA = 44,
- KEY_MINUS = 45,
- KEY_PERIOD = 46,
- KEY_SLASH = 47,
- KEY_0 = 48,
- KEY_1 = 49,
- KEY_2 = 50,
- KEY_3 = 51,
- KEY_4 = 52,
- KEY_5 = 53,
- KEY_6 = 54,
- KEY_7 = 55,
- KEY_8 = 56,
- KEY_9 = 57,
- KEY_COLON = 58,
- KEY_SEMICOLON = 59,
- KEY_LESS_THAN = 60,
- KEY_EQUALS = 61,
- KEY_GREATER_THAN = 62,
- KEY_QUESTION_MARK = 63,
- KEY_AT_SIGN = 64,
- KEY_A = 97,
- KEY_B = 98,
- KEY_C = 99,
- KEY_D = 100,
- KEY_E = 101,
- KEY_F = 102,
- KEY_G = 103,
- KEY_H = 104,
- KEY_I = 105,
- KEY_J = 106,
- KEY_K = 107,
- KEY_L = 108,
- KEY_M = 109,
- KEY_N = 110,
- KEY_O = 111,
- KEY_P = 112,
- KEY_Q = 113,
- KEY_R = 114,
- KEY_S = 115,
- KEY_T = 116,
- KEY_U = 117,
- KEY_V = 118,
- KEY_W = 119,
- KEY_X = 120,
- KEY_Y = 121,
- KEY_Z = 122,
- KEY_LEFT_BRACKET = 91,
- KEY_BACKSLASH = 92,
- KEY_RIGHT_BRACKET = 93,
- KEY_CARET = 94,
- KEY_UNDERSCORE = 95,
- KEY_GRAVE_ACCENT = 96,
- KEY_LEFT_BRACE = 123,
- KEY_PIPE = 124,
- KEY_RIGHT_BRACE = 125,
- KEY_TILDE = 126,
- KEY_DELETE = 127,
- KEY_UP = 128,
- KEY_DOWN = 129,
- KEY_LEFT = 130,
- KEY_RIGHT = 131,
- KEY_F1 = 132,
- KEY_F2 = 133,
- KEY_F3 = 134,
- KEY_F4 = 135,
- KEY_F5 = 136,
- KEY_F6 = 137,
- KEY_F7 = 138,
- KEY_F8 = 139,
- KEY_F9 = 140,
- KEY_F10 = 141,
- KEY_F11 = 142,
- KEY_F12 = 143,
- KEY_HOME = 144,
- KEY_END = 145,
- KEY_PAGE_UP = 146,
- KEY_PAGE_DOWN = 147,
- KEY_INSERT = 148,
- KEY_CAPS_LOCK = 149,
- KEY_NUM_LOCK = 150,
- KEY_SCROLL_LOCK = 151,
- KEY_PRINT_SCREEN = 152,
- KEY_PAUSE = 153,
- KEY_MENU = 154,
- KEY_LEFT_SHIFT = 155,
- KEY_RIGHT_SHIFT = 156,
- KEY_LEFT_CTRL = 157,
- KEY_RIGHT_CTRL = 158,
- KEY_LEFT_ALT = 159,
- KEY_RIGHT_ALT = 160,
- KEY_LEFT_SUPER = 161,
- KEY_RIGHT_SUPER = 162,
- KEY_KP_0 = 163,
- KEY_KP_1 = 164,
- KEY_KP_2 = 165,
- KEY_KP_3 = 166,
- KEY_KP_4 = 167,
- KEY_KP_5 = 168,
- KEY_KP_6 = 169,
- KEY_KP_7 = 170,
- KEY_KP_8 = 171,
- KEY_KP_9 = 172,
- KEY_KP_DECIMAL = 173,
- KEY_KP_DIVIDE = 174,
- KEY_KP_MULTIPLY = 175,
- KEY_KP_SUBTRACT = 176,
- KEY_KP_ADD = 177,
- KEY_KP_ENTER = 178,
- KEY_KP_EQUALS = 179
-} ta_keyboard_keys;
-
typedef struct {
ta_uint8 keys_down[TA_KEYBOARD_MAX_KEYS_DOWN];
} ta_keyboards;
diff --git a/include/ta_keys.h b/include/ta_keys.h
@@ -0,0 +1,134 @@
+#ifndef TA_KEYS_H
+#define TA_KEYS_H
+
+typedef enum {
+ KEY_NONE = 0,
+ KEY_BACKSPACE = 8,
+ KEY_TAB = 9,
+ KEY_ENTER = 13,
+ KEY_ESCAPE = 27,
+ KEY_SPACE = 32,
+ KEY_EXCLAMATION_MARK = 33,
+ KEY_QUOTATION_MARK = 34,
+ KEY_HASH = 35,
+ KEY_DOLLAR_SIGN = 36,
+ KEY_PERCENT_SIGN = 37,
+ KEY_AMPERSAND = 38,
+ KEY_APOSTROPHE = 39,
+ KEY_LEFT_PARENTHESIS = 40,
+ KEY_RIGHT_PARENTHESIS = 41,
+ KEY_ASTERISK = 42,
+ KEY_PLUS = 43,
+ KEY_COMMA = 44,
+ KEY_MINUS = 45,
+ KEY_PERIOD = 46,
+ KEY_SLASH = 47,
+ KEY_0 = 48,
+ KEY_1 = 49,
+ KEY_2 = 50,
+ KEY_3 = 51,
+ KEY_4 = 52,
+ KEY_5 = 53,
+ KEY_6 = 54,
+ KEY_7 = 55,
+ KEY_8 = 56,
+ KEY_9 = 57,
+ KEY_COLON = 58,
+ KEY_SEMICOLON = 59,
+ KEY_LESS_THAN = 60,
+ KEY_EQUALS = 61,
+ KEY_GREATER_THAN = 62,
+ KEY_QUESTION_MARK = 63,
+ KEY_AT_SIGN = 64,
+ KEY_A = 97,
+ KEY_B = 98,
+ KEY_C = 99,
+ KEY_D = 100,
+ KEY_E = 101,
+ KEY_F = 102,
+ KEY_G = 103,
+ KEY_H = 104,
+ KEY_I = 105,
+ KEY_J = 106,
+ KEY_K = 107,
+ KEY_L = 108,
+ KEY_M = 109,
+ KEY_N = 110,
+ KEY_O = 111,
+ KEY_P = 112,
+ KEY_Q = 113,
+ KEY_R = 114,
+ KEY_S = 115,
+ KEY_T = 116,
+ KEY_U = 117,
+ KEY_V = 118,
+ KEY_W = 119,
+ KEY_X = 120,
+ KEY_Y = 121,
+ KEY_Z = 122,
+ KEY_LEFT_BRACKET = 91,
+ KEY_BACKSLASH = 92,
+ KEY_RIGHT_BRACKET = 93,
+ KEY_CARET = 94,
+ KEY_UNDERSCORE = 95,
+ KEY_GRAVE_ACCENT = 96,
+ KEY_LEFT_BRACE = 123,
+ KEY_PIPE = 124,
+ KEY_RIGHT_BRACE = 125,
+ KEY_TILDE = 126,
+ KEY_DELETE = 127,
+ KEY_UP = 128,
+ KEY_DOWN = 129,
+ KEY_LEFT = 130,
+ KEY_RIGHT = 131,
+ KEY_F1 = 132,
+ KEY_F2 = 133,
+ KEY_F3 = 134,
+ KEY_F4 = 135,
+ KEY_F5 = 136,
+ KEY_F6 = 137,
+ KEY_F7 = 138,
+ KEY_F8 = 139,
+ KEY_F9 = 140,
+ KEY_F10 = 141,
+ KEY_F11 = 142,
+ KEY_F12 = 143,
+ KEY_HOME = 144,
+ KEY_END = 145,
+ KEY_PAGE_UP = 146,
+ KEY_PAGE_DOWN = 147,
+ KEY_INSERT = 148,
+ KEY_CAPS_LOCK = 149,
+ KEY_NUM_LOCK = 150,
+ KEY_SCROLL_LOCK = 151,
+ KEY_PRINT_SCREEN = 152,
+ KEY_PAUSE = 153,
+ KEY_MENU = 154,
+ KEY_LEFT_SHIFT = 155,
+ KEY_RIGHT_SHIFT = 156,
+ KEY_LEFT_CTRL = 157,
+ KEY_RIGHT_CTRL = 158,
+ KEY_LEFT_ALT = 159,
+ KEY_RIGHT_ALT = 160,
+ KEY_LEFT_SUPER = 161,
+ KEY_RIGHT_SUPER = 162,
+ KEY_KP_0 = 163,
+ KEY_KP_1 = 164,
+ KEY_KP_2 = 165,
+ KEY_KP_3 = 166,
+ KEY_KP_4 = 167,
+ KEY_KP_5 = 168,
+ KEY_KP_6 = 169,
+ KEY_KP_7 = 170,
+ KEY_KP_8 = 171,
+ KEY_KP_9 = 172,
+ KEY_KP_DECIMAL = 173,
+ KEY_KP_DIVIDE = 174,
+ KEY_KP_MULTIPLY = 175,
+ KEY_KP_SUBTRACT = 176,
+ KEY_KP_ADD = 177,
+ KEY_KP_ENTER = 178,
+ KEY_KP_EQUALS = 179
+} ta_keyboard_keys;
+
+#endif // TA_KEYS_H
diff --git a/include/ta_mod.h b/include/ta_mod.h
@@ -1,29 +0,0 @@
-#ifndef TA_MOD_H
-#define TA_MOD_H
-
-#include "ta_types.h"
-#include "ta_vm.h"
-
-#ifndef TA_MOD_RAM_SIZE
-#define TA_MOD_RAM_SIZE (256u * 1024u)
-#endif
-
-#ifndef TA_MOD_STEPS_PER_FRAME
-#define TA_MOD_STEPS_PER_FRAME 200000u
-#endif
-
-#define TA_SYS_EXIT 0
-#define TA_SYS_YIELD 1
-
-extern ta_bool ta_mod_running;
-
-ta_bool ta_mod_load(const ta_uint8 *binary, ta_size size);
-ta_bool ta_mod_load_file(const char *path);
-ta_bool ta_mod_update(void);
-ta_vm_status ta_mod_status(void);
-ta_int32 ta_mod_exit_code(void);
-const ta_vm *ta_mod_vm(void);
-
-void ta_mod_unload(void);
-
-#endif // TA_MOD_H
diff --git a/include/ta_scene.h b/include/ta_scene.h
@@ -0,0 +1,13 @@
+#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
@@ -3,14 +3,9 @@
#include "ta_types.h"
-typedef enum {
- TA_SCREEN_LOADING,
- TA_SCEEEN_MAIN_MENU
-} ta_screens;
-
-extern ta_screens ta_screen_current;
-
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);
diff --git a/include/ta_syscall.h b/include/ta_syscall.h
@@ -0,0 +1,10 @@
+#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_vm.h b/include/ta_vm.h
@@ -43,5 +43,22 @@ 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);
+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/platforms/saturn/jo.mk b/platforms/saturn/jo.mk
@@ -24,4 +24,4 @@ include $(COMPILER_DIR)/COMMON/jo_engine_makefile
_CCFLAGS = -I$(ROOT_DIR)/include -I$(SATURN_BUILD_DIR) -DTA_32BIT -DTA_DISABLE_BILINEAR_FILTERING
-$(OBJS): $(wildcard $(ROOT_DIR)/include/*.h $(ROOT_DIR)/include/screens/*.h) $(SATURN_BUILD_DIR)/ta_config.h
+$(OBJS): $(wildcard $(ROOT_DIR)/include/*.h) $(SATURN_BUILD_DIR)/ta_config.h
diff --git a/sdk/crt0.S b/sdk/crt0.S
@@ -1,14 +0,0 @@
- .section .text.start
- .globl _start
-
-_start:
- # Call the main function
- call main
-
- # If for some reason that returns, call the exit syscall
- li a7, 0
- ecall
-
- # If for some reason that fails, go into a infinite loop
-.loop:
- j .loop
diff --git a/sdk/sdk.mk b/sdk/sdk.mk
@@ -11,14 +11,15 @@ MARCH := rv32im
MABI := ilp32
LINKER_SCRIPT := linker.ld
+INCLUDES ?=
-CFLAGS := -O$(OPT) -march=$(MARCH) -mabi=$(MABI) -ffreestanding -fno-builtin -fno-pic -fno-stack-protector -nostdlib -Wall -Wextra -I$(SDK_DIR)
-ASFLAGS := -march=$(MARCH) -mabi=$(MABI)
-LDFLAGS := -nostdlib -static -T $(SDK_DIR)$(LINKER_SCRIPT) -Wl,--gc-sections
+CFLAGS := -O$(OPT) -march=$(MARCH) -mabi=$(MABI) -ffreestanding -fno-builtin -fno-pic -fno-stack-protector -fno-tree-loop-distribute-patterns -nostdlib -Wall -Wextra -I$(SDK_DIR) -I$(SDK_DIR)../include $(addprefix -I,$(INCLUDES))
+ASFLAGS := -march=$(MARCH) -mabi=$(MABI) -I$(SDK_DIR) -I$(SDK_DIR)../include
+LDFLAGS := -march=$(MARCH) -mabi=$(MABI) -nostdlib -static -T $(SDK_DIR)$(LINKER_SCRIPT) -Wl,--gc-sections
SRC_FILES := $(sort $(foreach s,$(SRCS),$(if $(wildcard $(s)/.),$(shell find $(patsubst %/,%,$(s)) -name '*.c'),$(s))))
-OBJS := $(BUILD)/crt0.o $(addprefix $(BUILD)/,$(SRC_FILES:.c=.o))
+OBJS := $(BUILD)/ta_runtime.o $(addprefix $(BUILD)/,$(SRC_FILES:.c=.o))
DEPS := $(OBJS:.o=.d)
all: $(BUILD)/$(TARGET).tab
@@ -33,6 +34,10 @@ $(BUILD)/%.o: %.c
@mkdir -p $(@D)
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
+$(BUILD)/%.o: $(SDK_DIR)%.c
+ @mkdir -p $(@D)
+ $(CC) $(CFLAGS) -MMD -MP -c $< -o $@
+
$(BUILD)/%.o: %.S
@mkdir -p $(@D)
$(CC) $(ASFLAGS) -MMD -MP -c $< -o $@
diff --git a/sdk/ta_config.h b/sdk/ta_config.h
@@ -0,0 +1,6 @@
+#ifndef TA_CONFIG_H
+#define TA_CONFIG_H
+
+#define TA_BITS 32
+
+#endif // TA_CONFIG_H
diff --git a/sdk/ta_runtime.c b/sdk/ta_runtime.c
@@ -0,0 +1,118 @@
+#include "tuxanci2.h"
+
+extern void main(void);
+
+typedef __SIZE_TYPE__ ta_runtime_size;
+
+void *memcpy(void *destination, const void *source, ta_runtime_size count) {
+ ta_uint8 *destination_bytes = (ta_uint8 *)destination;
+ const ta_uint8 *source_bytes = (const ta_uint8 *)source;
+
+ for (ta_runtime_size i = 0; i < count; i++) {
+ destination_bytes[i] = source_bytes[i];
+ }
+
+ return destination;
+}
+
+void *memmove(void *destination, const void *source, ta_runtime_size count) {
+ ta_uint8 *destination_bytes = (ta_uint8 *)destination;
+ const ta_uint8 *source_bytes = (const ta_uint8 *)source;
+
+ if (destination_bytes < source_bytes) {
+ for (ta_runtime_size i = 0; i < count; i++) {
+ destination_bytes[i] = source_bytes[i];
+ }
+ } else {
+ for (ta_runtime_size i = count; i > 0; i--) {
+ destination_bytes[i - 1] = source_bytes[i - 1];
+ }
+ }
+
+ return destination;
+}
+
+void *memset(void *destination, int value, ta_runtime_size count) {
+ ta_uint8 *destination_bytes = (ta_uint8 *)destination;
+
+ for (ta_runtime_size i = 0; i < count; i++) {
+ destination_bytes[i] = (ta_uint8)value;
+ }
+
+ return destination;
+}
+
+int memcmp(const void *first, const void *second, ta_runtime_size count) {
+ const ta_uint8 *first_bytes = (const ta_uint8 *)first;
+ const ta_uint8 *second_bytes = (const ta_uint8 *)second;
+
+ for (ta_runtime_size i = 0; i < count; i++) {
+ if (first_bytes[i] != second_bytes[i]) {
+ return first_bytes[i] < second_bytes[i] ? -1 : 1;
+ }
+ }
+
+ return 0;
+}
+
+static ta_uint64 divide_unsigned(ta_uint64 numerator, ta_uint64 denominator, ta_uint64 *remainder_out) {
+ if (denominator == 0) {
+ *remainder_out = numerator;
+
+ return ~(ta_uint64)0;
+ }
+
+ ta_uint64 quotient = 0;
+ ta_uint64 remainder = 0;
+
+ for (ta_int32 bit = 63; bit >= 0; bit--) {
+ ta_uint64 carry = remainder >> 63;
+
+ remainder = (remainder << 1) | ((numerator >> bit) & 1);
+
+ if (carry != 0 || remainder >= denominator) {
+ remainder -= denominator;
+ quotient |= (ta_uint64)1 << bit;
+ }
+ }
+
+ *remainder_out = remainder;
+
+ return quotient;
+}
+
+ta_uint64 __udivdi3(ta_uint64 numerator, ta_uint64 denominator) {
+ ta_uint64 remainder;
+
+ return divide_unsigned(numerator, denominator, &remainder);
+}
+
+ta_uint64 __umoddi3(ta_uint64 numerator, ta_uint64 denominator) {
+ ta_uint64 remainder;
+
+ divide_unsigned(numerator, denominator, &remainder);
+
+ return remainder;
+}
+
+ta_int64 __divdi3(ta_int64 numerator, ta_int64 denominator) {
+ ta_bool negative = (numerator < 0) != (denominator < 0);
+ ta_uint64 numerator_magnitude = numerator < 0 ? -(ta_uint64)numerator : (ta_uint64)numerator;
+ ta_uint64 denominator_magnitude = denominator < 0 ? -(ta_uint64)denominator : (ta_uint64)denominator;
+ ta_uint64 quotient = __udivdi3(numerator_magnitude, denominator_magnitude);
+
+ return negative ? (ta_int64)(-quotient) : (ta_int64)quotient;
+}
+
+ta_int64 __moddi3(ta_int64 numerator, ta_int64 denominator) {
+ ta_uint64 numerator_magnitude = numerator < 0 ? -(ta_uint64)numerator : (ta_uint64)numerator;
+ ta_uint64 denominator_magnitude = denominator < 0 ? -(ta_uint64)denominator : (ta_uint64)denominator;
+ ta_uint64 remainder = __umoddi3(numerator_magnitude, denominator_magnitude);
+
+ return numerator < 0 ? (ta_int64)(-remainder) : (ta_int64)remainder;
+}
+
+__attribute__((section(".text.start"))) void _start(void) {
+ main();
+ ta_ready(ta_return);
+}
diff --git a/sdk/tuxanci2.h b/sdk/tuxanci2.h
@@ -1,32 +1,102 @@
#ifndef TUXANCI2_H
#define TUXANCI2_H
-#include <stdint.h>
+#include "ta_abi.h"
+#include "ta_keys.h"
+#include "ta_types.h"
-#define TA_SYS_EXIT 0
-#define TA_SYS_YIELD 1
+#define TA_RGB(red, green, blue) ((((ta_uint32)(red) & 255u) << 16) | (((ta_uint32)(green) & 255u) << 8) | ((ta_uint32)(blue) & 255u))
-static inline int32_t ta_syscall(uint32_t num, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4) {
- register uint32_t r0 __asm__("a0") = a0;
- register uint32_t r1 __asm__("a1") = a1;
- register uint32_t r2 __asm__("a2") = a2;
- register uint32_t r3 __asm__("a3") = a3;
- register uint32_t r4 __asm__("a4") = a4;
- register uint32_t r7 __asm__("a7") = num;
+typedef ta_uint32 ta_text_handle;
- __asm__ volatile("ecall" : "+r"(r0) : "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7) : "memory");
+typedef void (*ta_screen_init_function)(void);
+typedef void (*ta_screen_update_function)(void);
+typedef void (*ta_screen_destroy_function)(void);
- return (int32_t)r0;
+static inline ta_uint32 ta_syscall(ta_uint32 number, ta_uint32 arg0, ta_uint32 arg1, ta_uint32 arg2, ta_uint32 arg3, ta_uint32 arg4, ta_uint32 arg5) {
+ register ta_uint32 reg0 __asm__("a0") = arg0;
+ register ta_uint32 reg1 __asm__("a1") = arg1;
+ register ta_uint32 reg2 __asm__("a2") = arg2;
+ register ta_uint32 reg3 __asm__("a3") = arg3;
+ register ta_uint32 reg4 __asm__("a4") = arg4;
+ register ta_uint32 reg5 __asm__("a5") = arg5;
+ register ta_uint32 reg7 __asm__("a7") = number;
+
+ __asm__ volatile("ecall" : "+r"(reg0) : "r"(reg1), "r"(reg2), "r"(reg3), "r"(reg4), "r"(reg5), "r"(reg7) : "memory");
+
+ return reg0;
}
-static inline void ta_exit(int32_t code) {
- ta_syscall(TA_SYS_EXIT, (uint32_t)code, 0, 0, 0, 0);
+static inline void ta_exit(ta_int32 code) {
+ ta_syscall(TA_SYS_EXIT, (ta_uint32)code, 0, 0, 0, 0, 0);
while (1) {}
}
-static inline void ta_yield(void) {
- ta_syscall(TA_SYS_YIELD, 0, 0, 0, 0, 0);
+static inline void ta_ready(void (*stub)(ta_uint32)) {
+ ta_syscall(TA_SYS_READY, (ta_uint32)(ta_size)stub, 0, 0, 0, 0, 0);
+}
+
+static inline void ta_return(ta_uint32 value) {
+ ta_syscall(TA_SYS_RETURN, value, 0, 0, 0, 0, 0);
+
+ while (1) {}
+}
+
+static inline ta_uint32 ta_display_width(void) {
+ return ta_syscall(TA_SYS_DISPLAY_WIDTH, 0, 0, 0, 0, 0, 0);
+}
+
+static inline ta_uint32 ta_display_height(void) {
+ return ta_syscall(TA_SYS_DISPLAY_HEIGHT, 0, 0, 0, 0, 0, 0);
+}
+
+static inline ta_bool ta_display_sprite_mode(void) {
+ return ta_syscall(TA_SYS_DISPLAY_SPRITE_MODE, 0, 0, 0, 0, 0, 0) != 0;
+}
+
+static inline ta_bool ta_keyboard_is_down(ta_keyboard_keys key) {
+ return ta_syscall(TA_SYS_KEYBOARD_IS_DOWN, (ta_uint32)key, 0, 0, 0, 0, 0) != 0;
+}
+
+static inline ta_bool ta_image_init(ta_uint32 asset) {
+ return ta_syscall(TA_SYS_IMAGE_INIT, asset, 0, 0, 0, 0, 0) != 0;
+}
+
+static inline ta_bool ta_image_draw(ta_uint32 asset, ta_fixed x, ta_fixed y, ta_fixed width, ta_fixed height, ta_uint16 frame) {
+ return ta_syscall(TA_SYS_IMAGE_DRAW, asset, x, y, width, height, frame) != 0;
+}
+
+static inline ta_bool ta_image_get_size(ta_uint32 asset, ta_uint32 *width, ta_uint32 *height) {
+ return ta_syscall(TA_SYS_IMAGE_GET_SIZE, asset, (ta_uint32)(ta_size)width, (ta_uint32)(ta_size)height, 0, 0, 0) != 0;
+}
+
+static inline ta_text_handle ta_text_create(const char *text, ta_uint32 size, ta_uint32 color, ta_uint32 border_width, ta_uint32 border_color) {
+ return ta_syscall(TA_SYS_TEXT_CREATE, (ta_uint32)(ta_size)text, size, color, border_width, border_color, 0);
+}
+
+static inline ta_uint32 ta_text_width(ta_text_handle text) {
+ return ta_syscall(TA_SYS_TEXT_WIDTH, text, 0, 0, 0, 0, 0);
+}
+
+static inline ta_uint32 ta_text_height(ta_text_handle text) {
+ return ta_syscall(TA_SYS_TEXT_HEIGHT, text, 0, 0, 0, 0, 0);
+}
+
+static inline void ta_text_draw(ta_text_handle text, ta_uint32 x, ta_uint32 y) {
+ ta_syscall(TA_SYS_TEXT_DRAW, text, x, y, 0, 0, 0);
+}
+
+static inline void ta_text_destroy(ta_text_handle text) {
+ ta_syscall(TA_SYS_TEXT_DESTROY, text, 0, 0, 0, 0, 0);
+}
+
+static inline ta_bool ta_screen_register(ta_uint32 id, ta_screen_init_function init, ta_screen_update_function update, ta_screen_destroy_function destroy) {
+ return ta_syscall(TA_SYS_SCREEN_REGISTER, id, (ta_uint32)(ta_size)init, (ta_uint32)(ta_size)update, (ta_uint32)(ta_size)destroy, 0, 0) != 0;
+}
+
+static inline ta_bool ta_screen_switch(ta_uint32 id) {
+ return ta_syscall(TA_SYS_SCREEN_SWITCH, id, 0, 0, 0, 0, 0) != 0;
}
#endif // TUXANCI2_H
diff --git a/src/screens/ta_screen_loading.c b/src/screens/ta_screen_loading.c
@@ -1,72 +0,0 @@
-#include "ta_display.h"
-#include "ta_fb.h"
-#include "ta_files.h"
-#include "ta_image.h"
-#include "ta_screen.h"
-
-#define TA_LOADING_LOGO_SIZE TA_FIXED(0.3)
-
-static ta_bool logo_loaded;
-static ta_bool logo_shown;
-static ta_bool background_loaded;
-
-static void draw_loading_logo(void) {
- ta_uint32 width;
- ta_uint32 height;
-
- if (!ta_image_get_size(ta_file_loading_logo, ta_file_loading_logo_len, &width, &height)) {
- return;
- }
-
- ta_uint64 frame_width = ta_display->width;
- ta_uint64 frame_height = ta_display->height;
- ta_fixed draw_width;
- ta_fixed draw_height;
-
- if (frame_width * height <= frame_height * width) {
- draw_width = TA_LOADING_LOGO_SIZE;
- draw_height = (ta_fixed)((TA_LOADING_LOGO_SIZE * (ta_uint64)height * frame_width) / ((ta_uint64)width * frame_height));
- } else {
- draw_height = TA_LOADING_LOGO_SIZE;
- draw_width = (ta_fixed)((TA_LOADING_LOGO_SIZE * (ta_uint64)width * frame_height) / ((ta_uint64)height * frame_width));
- }
-
- ta_fixed x = (TA_FIXED_ONE - draw_width) / 2;
- ta_fixed y = (TA_FIXED_ONE - draw_height) / 2;
-
- ta_image_draw(ta_file_loading_logo, ta_file_loading_logo_len, x, y, draw_width, draw_height, 0);
-}
-
-void ta_screen_loading_init(void) {
- logo_loaded = ta_false;
- logo_shown = ta_false;
- background_loaded = ta_false;
-}
-
-ta_bool ta_screen_loading_update(void) {
- ta_fb_clear(ta_display);
-
- if (!logo_loaded) {
- logo_loaded = ta_image_init(ta_file_loading_logo, ta_file_loading_logo_len);
- return ta_true;
- }
-
- draw_loading_logo();
-
- if (!logo_shown) {
- logo_shown = ta_true;
- return ta_true;
- }
-
- if (!background_loaded) {
- background_loaded = ta_image_init(ta_file_bg, ta_file_bg_len);
- return ta_true;
- }
-
- ta_screen_current = TA_SCEEEN_MAIN_MENU;
-
- return ta_true;
-}
-
-void ta_screen_loading_destroy(void) {
-}
diff --git a/src/screens/ta_screen_main_menu.c b/src/screens/ta_screen_main_menu.c
@@ -1,54 +0,0 @@
-#include "ta_button.h"
-#include "ta_display.h"
-#include "ta_fb.h"
-#include "ta_files.h"
-#include "ta_image.h"
-#include "ta_main.h"
-#include "ta_screen.h"
-
-static ta_button_lists menu;
-static ta_bool needs_redraw;
-static ta_bool background_in_sprites;
-
-void ta_screen_main_menu_init(void) {
- needs_redraw = ta_true;
- background_in_sprites = ta_false;
-
- ta_button_lists_init(&menu);
- ta_button_lists_add(&menu, "Play", 0);
- ta_button_lists_add(&menu, "Quit", ta_main_destroy);
-}
-
-ta_bool ta_screen_main_menu_update(void) {
- if (ta_button_lists_update(&menu)) {
- needs_redraw = ta_true;
- }
-
- if (!needs_redraw) {
- return ta_false;
- }
-
- needs_redraw = ta_false;
-
- if (ta_display->sprite_mode && background_in_sprites && ta_display_sprites != 0 && ta_display_sprites->count >= 1) {
- while (ta_display_sprites->count > 1) {
- ta_display_sprite_free(ta_display_sprites->sprites[ta_display_sprites->count - 1]);
- }
- } else {
- ta_display_sprites_clear();
-
- if (!ta_image_draw(ta_file_bg, ta_file_bg_len, 0, 0, TA_FIXED_ONE, TA_FIXED_ONE, 0)) {
- ta_fb_clear(ta_display);
- } else {
- background_in_sprites = ta_display->sprite_mode;
- }
- }
-
- ta_button_lists_draw(&menu);
-
- return ta_true;
-}
-
-void ta_screen_main_menu_destroy(void) {
- ta_button_lists_destroy(&menu);
-}
diff --git a/src/ta_button.c b/src/ta_button.c
@@ -1,160 +0,0 @@
-#include "ta_button.h"
-#include "ta_api.h"
-#include "ta_text.h"
-
-#define TA_BUTTON_FONT_SIZE 32
-#define TA_BUTTON_SPACING 48
-#define TA_BUTTON_COLOR_IDLE 118
-#define TA_BUTTON_COLOR_SELECTED 255
-
-static ta_uint32 scale_to_display(ta_uint32 value) {
- ta_uint32 numerator = (ta_uint32)ta_display->width;
- ta_uint32 denominator = 800;
-
- if ((ta_uint64)ta_display->width * 600 >= (ta_uint64)ta_display->height * 800) {
- numerator = (ta_uint32)ta_display->height;
- denominator = 600;
- }
-
- ta_uint32 scaled = (value * numerator * 2 + denominator) / (denominator * 2);
-
- return scaled == 0 ? 1 : scaled;
-}
-
-void ta_button_lists_init(ta_button_lists *list) {
- if (list == 0) {
- return;
- }
-
- list->buttons = 0;
- list->size = 0;
- list->selected = 0;
- list->animation_from = 0;
- list->animation_frame = 0;
- list->animation_active = ta_false;
- list->up_was_down = ta_false;
- list->down_was_down = ta_false;
- list->enter_was_down = ta_false;
-}
-
-ta_bool ta_button_lists_add(ta_button_lists *list, const char *label, ta_button_callback callback) {
- if (list == 0 || label == 0 || list->size == (ta_size)-1 || list->size + 1 > (ta_size)-1 / sizeof(ta_button)) {
- return ta_false;
- }
-
- ta_size new_size = list->size + 1;
- ta_button *buttons = (ta_button *)ta_malloc(new_size * sizeof(ta_button));
-
- if (buttons == 0) {
- return ta_false;
- }
-
- for (ta_size i = 0; i < list->size; i++) {
- buttons[i] = list->buttons[i];
- }
-
- buttons[list->size].label = label;
- buttons[list->size].callback = callback;
- buttons[list->size].text_idle.text = label;
- buttons[list->size].text_idle.size = (ta_uint8)(scale_to_display(TA_BUTTON_FONT_SIZE) > 255 ? 255 : scale_to_display(TA_BUTTON_FONT_SIZE));
- buttons[list->size].text_idle.red = TA_BUTTON_COLOR_IDLE;
- buttons[list->size].text_idle.green = TA_BUTTON_COLOR_IDLE;
- buttons[list->size].text_idle.blue = TA_BUTTON_COLOR_IDLE;
- buttons[list->size].text_idle.border_width = 1;
- buttons[list->size].text_idle.border_red = 0;
- buttons[list->size].text_idle.border_green = 0;
- buttons[list->size].text_idle.border_blue = 0;
- buttons[list->size].text_idle.sprite = 0;
- ta_text_rasterize(&buttons[list->size].text_idle);
-
- buttons[list->size].text_selected.text = label;
- buttons[list->size].text_selected.size = (ta_uint8)(scale_to_display(TA_BUTTON_FONT_SIZE) > 255 ? 255 : scale_to_display(TA_BUTTON_FONT_SIZE));
- buttons[list->size].text_selected.red = TA_BUTTON_COLOR_SELECTED;
- buttons[list->size].text_selected.green = TA_BUTTON_COLOR_SELECTED;
- buttons[list->size].text_selected.blue = TA_BUTTON_COLOR_SELECTED;
- buttons[list->size].text_selected.border_width = 1;
- buttons[list->size].text_selected.border_red = 0;
- buttons[list->size].text_selected.border_green = 0;
- buttons[list->size].text_selected.border_blue = 0;
- buttons[list->size].text_selected.sprite = 0;
- ta_text_rasterize(&buttons[list->size].text_selected);
-
- ta_free(list->buttons);
-
- list->buttons = buttons;
- list->size = new_size;
-
- return ta_true;
-}
-
-ta_bool ta_button_lists_update(ta_button_lists *list) {
- if (list == 0 || list->size == 0) {
- return ta_false;
- }
-
- ta_bool moved = ta_false;
- ta_bool up_down = ta_keyboard_is_down(KEY_UP);
- ta_bool down_down = ta_keyboard_is_down(KEY_DOWN);
- ta_bool enter_down = ta_keyboard_is_down(KEY_ENTER);
- ta_bool up_pressed = up_down && !list->up_was_down;
- ta_bool down_pressed = down_down && !list->down_was_down;
- ta_bool enter_pressed = enter_down && !list->enter_was_down;
- list->up_was_down = up_down;
- list->down_was_down = down_down;
- list->enter_was_down = enter_down;
-
- if (down_pressed || up_pressed) {
- if (down_pressed) {
- list->selected = (list->selected + 1) % list->size;
- } else {
- list->selected = list->selected == 0 ? list->size - 1 : list->selected - 1;
- }
- moved = ta_true;
- }
-
- if (enter_pressed && list->buttons[list->selected].callback != 0) {
- list->buttons[list->selected].callback();
- }
-
- return moved;
-}
-
-void ta_button_lists_draw(const ta_button_lists *list) {
- if (list == 0 || list->size == 0) {
- return;
- }
-
- ta_int32 spacing = (ta_int32)scale_to_display(TA_BUTTON_SPACING);
- ta_int32 center_y = (ta_int32)(ta_display->height / 2);
- ta_int32 total_span = (ta_int32)(list->size - 1) * spacing;
- ta_int32 start_y = center_y - (total_span / 2);
-
- for (ta_size i = 0; i < list->size; i++) {
- ta_button *btn = &((ta_button_lists *)list)->buttons[i];
- ta_text *txt = (i == list->selected) ? &btn->text_selected : &btn->text_idle;
-
- ta_int32 y = start_y + (ta_int32)i * spacing - (ta_int32)(txt->height / 2);
-
- if (y < 0 || (ta_size)y >= ta_display->height || txt->width > ta_display->width) {
- continue;
- }
-
- txt->x = (ta_uint32)(ta_display->width - txt->width) / 2;
- txt->y = (ta_uint32)y;
- ta_text_draw(txt);
- }
-}
-
-void ta_button_lists_destroy(ta_button_lists *list) {
- if (list == 0) {
- return;
- }
-
- for (ta_size i = 0; i < list->size; i++) {
- ta_text_destroy(&list->buttons[i].text_idle);
- ta_text_destroy(&list->buttons[i].text_selected);
- }
-
- ta_free(list->buttons);
- ta_button_lists_init(list);
-}
diff --git a/src/ta_main.c b/src/ta_main.c
@@ -1,22 +1,28 @@
#include "ta_api.h"
-#include "ta_files.h"
+#include "ta_main.h"
+#include "ta_vm.h"
#include "ta_screen.h"
-#include "ta_mod.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();
- ta_mod_load_file("tuxanci2.tab");
+
+ if (!ta_vm_load_file("game/tuxanci2.tab")) {
+ ta_exit(1);
+ }
}
ta_bool ta_main_update(void) {
- if (ta_mod_running) {
- ta_mod_update();
+ ta_bool drawn = ta_screen_update();
+
+ if (!ta_vm_running) {
+ ta_main_destroy();
}
- if (!ta_screen_update()) {
+ if (!drawn) {
return ta_false;
}
@@ -26,8 +32,11 @@ ta_bool ta_main_update(void) {
}
void ta_main_destroy(void) {
+ ta_int32 exit_code = ta_vm_exit_code();
+
ta_screen_destroy();
+ ta_vm_unload();
ta_display_deinit();
- ta_exit(0);
+ ta_exit(exit_code);
}
diff --git a/src/ta_mod.c b/src/ta_mod.c
@@ -1,119 +0,0 @@
-#include "ta_mod.h"
-#include "ta_api.h"
-#include "ta_display.h"
-#include "ta_fb.h"
-
-static ta_vm vm;
-static ta_uint8 *ram;
-static ta_bool loaded;
-static ta_vm_status status;
-static ta_int32 exit_code;
-ta_bool ta_mod_running;
-
-static ta_vm_status mod_ecall(ta_vm *machine, void *user) {
- (void)user;
-
- ta_uint32 *r = machine->regs;
- ta_uint32 *args = &r[TA_VM_REG_A0];
-
- switch (r[TA_VM_REG_A7]) {
- case TA_SYS_EXIT: {
- exit_code = (ta_int32)args[0];
-
- return TA_VM_HALTED;
- }
- case TA_SYS_YIELD: {
- machine->pc += 4;
-
- return TA_VM_YIELDED;
- }
- default: {
- return TA_VM_FAULT_ILLEGAL;
- }
- }
-}
-
-ta_bool ta_mod_load(const ta_uint8 *binary, ta_size size) {
- ta_mod_unload();
-
- if (!binary || size < 4 || size > TA_MOD_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_MOD_RAM_SIZE);
-
- if (!ram) {
- return ta_false;
- }
-
- for (ta_size i = 0; i < TA_MOD_RAM_SIZE; i++) {
- ram[i] = i < size ? binary[i] : 0;
- }
-
- ta_vm_init(&vm, ram, TA_MOD_RAM_SIZE, mod_ecall, TA_NULL);
- vm.pc = 4;
- status = TA_VM_RUNNING;
- exit_code = 0;
- loaded = ta_true;
- ta_mod_running = loaded && (status == TA_VM_RUNNING);
-
- return ta_true;
-}
-
-ta_bool ta_mod_load_file(const char *path) {
- ta_fs_file file = ta_read_file(path);
-
- if (!file.data) {
- return ta_false;
- }
-
- ta_bool ok = ta_mod_load((const ta_uint8 *)file.data, file.size);
-
- ta_close_file(&file);
-
- return ok;
-}
-
-ta_bool ta_mod_update(void) {
- if (!loaded || status != TA_VM_RUNNING) {
- return ta_false;
- }
-
- status = ta_vm_run(&vm, TA_MOD_STEPS_PER_FRAME);
-
- if (status == TA_VM_YIELDED) {
- status = TA_VM_RUNNING;
- }
-
- ta_mod_running = loaded && (status == TA_VM_RUNNING);
-
- return ta_mod_running;
-}
-
-ta_vm_status ta_mod_status(void) {
- return status;
-}
-
-ta_int32 ta_mod_exit_code(void) {
- return exit_code;
-}
-
-const ta_vm *ta_mod_vm(void) {
- return &vm;
-}
-
-void ta_mod_unload(void) {
- if (ram) {
- ta_free(ram);
- ram = TA_NULL;
- }
-
- loaded = ta_false;
- status = TA_VM_HALTED;
- ta_mod_running = ta_false;
-}
diff --git a/src/ta_scene.c b/src/ta_scene.c
@@ -0,0 +1,173 @@
+#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,41 +1,136 @@
#include "ta_screen.h"
+#include "ta_abi.h"
#include "ta_image.h"
-#include "ta_types.h"
+#include "ta_vm.h"
+#include "ta_scene.h"
+#include "ta_api.h"
-#include "screens/ta_screen_loading.h"
-#include "screens/ta_screen_main_menu.h"
+#define TA_SCREEN_NONE (-1)
+#define TA_SCREEN_MAX_TRANSITIONS 16
-ta_screens ta_screen_current;
+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) {
- ta_screen_current = TA_SCREEN_LOADING;
- ta_screen_loading_init();
+ 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) {
- switch (ta_screen_current) {
- case TA_SCREEN_LOADING: {
- ta_bool drawn = ta_screen_loading_update();
- if (ta_screen_current == TA_SCEEEN_MAIN_MENU) {
- ta_screen_loading_destroy();
- ta_screen_main_menu_init();
+ 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;
}
- return drawn;
}
- case TA_SCEEEN_MAIN_MENU: {
- return ta_screen_main_menu_update();
- }
- default: {
- 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) {
- if (ta_screen_current == TA_SCREEN_LOADING) {
- ta_screen_loading_destroy();
- } else if (ta_screen_current == TA_SCEEEN_MAIN_MENU) {
- ta_screen_main_menu_destroy();
- }
+ 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
@@ -0,0 +1,274 @@
+#include "ta_syscall.h"
+#include "ta_abi.h"
+#include "ta_api.h"
+#include "ta_display.h"
+#include "ta_fb.h"
+#include "ta_files.h"
+#include "ta_image.h"
+#include "ta_keyboard.h"
+#include "ta_scene.h"
+#include "ta_screen.h"
+#include "ta_text.h"
+
+static ta_text **texts;
+static ta_uint32 text_count;
+
+static ta_bool asset_get(ta_uint32 id, const ta_uint8 **data, ta_uint32 *length) {
+ switch (id) {
+ case TA_ASSET_BG: {
+ *data = ta_file_bg;
+ *length = ta_file_bg_len;
+
+ return ta_true;
+ }
+ case TA_ASSET_LOADING_LOGO: {
+ *data = ta_file_loading_logo;
+ *length = ta_file_loading_logo_len;
+
+ return ta_true;
+ }
+ default: {
+ return ta_false;
+ }
+ }
+}
+
+static ta_text *text_get(ta_uint32 handle) {
+ if (handle == 0 || handle > text_count) {
+ return TA_NULL;
+ }
+
+ return texts[handle - 1];
+}
+
+static ta_bool text_slot_find(ta_uint32 *slot) {
+ for (ta_uint32 i = 0; i < text_count; i++) {
+ if (texts[i] == TA_NULL) {
+ *slot = i;
+
+ return ta_true;
+ }
+ }
+
+ ta_text **grown = (ta_text **)ta_malloc((ta_size)(text_count + 1) * sizeof(ta_text *));
+
+ if (grown == TA_NULL) {
+ return ta_false;
+ }
+
+ for (ta_uint32 i = 0; i < text_count; i++) {
+ grown[i] = texts[i];
+ }
+
+ grown[text_count] = TA_NULL;
+
+ if (texts != TA_NULL) {
+ ta_free(texts);
+ }
+
+ texts = grown;
+ *slot = text_count;
+ text_count++;
+
+ return ta_true;
+}
+
+static ta_uint32 text_create(const char *string, ta_uint32 size, ta_uint32 color, ta_uint32 border_width, ta_uint32 border_color) {
+ if (size == 0) {
+ return 0;
+ }
+
+ ta_text *text = (ta_text *)ta_malloc(sizeof(ta_text));
+
+ if (text == TA_NULL) {
+ return 0;
+ }
+
+ text->text = string;
+ text->x = 0;
+ text->y = 0;
+ text->size = (ta_uint8)size;
+ text->red = (ta_uint8)((color >> 16) & 255);
+ text->green = (ta_uint8)((color >> 8) & 255);
+ text->blue = (ta_uint8)(color & 255);
+ text->border_width = (ta_uint8)border_width;
+ text->border_red = (ta_uint8)((border_color >> 16) & 255);
+ text->border_green = (ta_uint8)((border_color >> 8) & 255);
+ text->border_blue = (ta_uint8)(border_color & 255);
+ text->width = 0;
+ text->height = 0;
+ text->sprite = TA_NULL;
+
+ ta_text_rasterize(text);
+
+ text->text = TA_NULL;
+
+ if (!text->sprite) {
+ ta_free(text);
+
+ return 0;
+ }
+
+ ta_uint32 slot;
+
+ if (!text_slot_find(&slot)) {
+ ta_text_destroy(text);
+ ta_free(text);
+
+ return 0;
+ }
+
+ texts[slot] = text;
+
+ return slot + 1;
+}
+
+static void text_destroy(ta_uint32 handle) {
+ ta_text *text = text_get(handle);
+
+ if (!text) {
+ return;
+ }
+
+ ta_scene_invalidate();
+ ta_text_destroy(text);
+ ta_free(text);
+ texts[handle - 1] = TA_NULL;
+}
+
+ta_vm_status ta_syscall(ta_vm *vm) {
+ ta_uint32 *registers = vm->regs;
+ ta_uint32 *args = ®isters[TA_VM_REG_A0];
+ ta_uint32 result = 0;
+
+ switch (registers[TA_VM_REG_A7]) {
+ case TA_SYS_DISPLAY_WIDTH: {
+ result = ta_display->width;
+
+ break;
+ }
+ case TA_SYS_DISPLAY_HEIGHT: {
+ result = ta_display->height;
+
+ break;
+ }
+ case TA_SYS_DISPLAY_SPRITE_MODE: {
+ result = ta_display->sprite_mode ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_KEYBOARD_IS_DOWN: {
+ result = args[0] < 256 && ta_keyboard_is_down((ta_keyboard_keys)args[0]) ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_IMAGE_INIT: {
+ const ta_uint8 *data;
+ ta_uint32 length;
+
+ result = asset_get(args[0], &data, &length) && ta_image_init(data, length) ? 1 : 0;
+
+ break;
+ }
+ case TA_SYS_IMAGE_DRAW: {
+ const ta_uint8 *data;
+ ta_uint32 length;
+
+ result = asset_get(args[0], &data, &length) && 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 (!asset_get(args[0], &data, &length) || !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();
+
+ 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_vm.c b/src/ta_vm.c
@@ -1,4 +1,7 @@
#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
@@ -162,6 +165,20 @@ static ta_bool store_value(ta_vm *vm, ta_uint32 addr, ta_uint32 size, ta_uint32
return ta_true;
}
+const char *ta_vm_cstr(const ta_vm *vm, ta_uint32 addr) {
+ if (addr >= vm->mem_size) {
+ return TA_NULL;
+ }
+
+ for (ta_uint32 offset = addr; offset < vm->mem_size; offset++) {
+ if (vm->mem[offset] == 0) {
+ return (const char *)(vm->mem + addr);
+ }
+ }
+
+ 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;
@@ -439,3 +456,158 @@ ta_vm_status ta_vm_run(ta_vm *vm, ta_uint32 max_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;
+}