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:
MMakefile | 2+-
MREADME.md | 12++++++++++++
Mgame/Makefile | 5+++--
Mgame/src/main.c | 11++++++-----
Agame/src/screens/ta_screen_loading.c | 40++++++++++++++++++++++++++++++++++++++++
Agame/src/screens/ta_screen_loading.h | 10++++++++++
Agame/src/screens/ta_screen_main_menu.c | 27+++++++++++++++++++++++++++
Agame/src/screens/ta_screen_main_menu.h | 10++++++++++
Agame/src/ta_button.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agame/src/ta_button.h | 34++++++++++++++++++++++++++++++++++
Agame/src/ta_screens.h | 9+++++++++
Dinclude/screens/ta_screen_loading.h | 10----------
Dinclude/screens/ta_screen_main_menu.h | 10----------
Ainclude/ta_abi.h | 25+++++++++++++++++++++++++
Dinclude/ta_button.h | 34----------------------------------
Minclude/ta_keyboard.h | 131+------------------------------------------------------------------------------
Ainclude/ta_keys.h | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dinclude/ta_mod.h | 29-----------------------------
Ainclude/ta_scene.h | 13+++++++++++++
Minclude/ta_screen.h | 9++-------
Ainclude/ta_syscall.h | 10++++++++++
Minclude/ta_vm.h | 17+++++++++++++++++
Mplatforms/saturn/jo.mk | 2+-
Dsdk/crt0.S | 14--------------
Msdk/sdk.mk | 13+++++++++----
Asdk/ta_config.h | 6++++++
Asdk/ta_runtime.c | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msdk/tuxanci2.h | 102++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Dsrc/screens/ta_screen_loading.c | 72------------------------------------------------------------------------
Dsrc/screens/ta_screen_main_menu.c | 54------------------------------------------------------
Dsrc/ta_button.c | 160-------------------------------------------------------------------------------
Msrc/ta_main.c | 23++++++++++++++++-------
Dsrc/ta_mod.c | 119-------------------------------------------------------------------------------
Asrc/ta_scene.c | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ta_screen.c | 141++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Asrc/ta_syscall.c | 274+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ta_vm.c | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 = &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; + + 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; +}