/shell/main.c
#include <sysops.h>
#include <console.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

struct MemoryRange {
	uint32_t start;
	uint32_t length;
};

void svc64(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3);

__asm__(
".global svc64\n"
".type svc64,function\n"
"svc64:\n"
"	svc #64\n"
"	bx lr\n"
);

int peek(lua_State *L) {
	int n_args = lua_gettop(L);
	if (n_args != 1) {
		puts("peek(addr)");
		return 0;
	}
	uint32_t addr = lua_tointeger(L, 1);
	uint32_t data = *(uint32_t *)addr;
	lua_pushinteger(L, data);
	return 1;
}

int poke(lua_State *L) {
	int n_args = lua_gettop(L);
	if (n_args != 2) {
		puts("poke(addr, value)");
		return 0;
	}
	uint32_t addr = lua_tointeger(L, 1);
	uint32_t data = lua_tointeger(L, 2);
	*(uint32_t *)addr = data;
	return 0;
}

int keydebug() {
	while (1) {
		int k = sys_ops->console->readkey();
		if (k == -1) continue;
		if (is_command_key(k)) {
			switch(k) {
			case KEY_UP: puts("[up]"); break;
			case KEY_DOWN: puts("[down]"); break;
			case KEY_LEFT: puts("[left]"); break;
			case KEY_RIGHT: puts("[right]"); break;
			case KEY_ENTER: puts("[enter]"); break;
			case KEY_BACKSPACE: puts("[backspace]"); break;
			case KEY_TAB: puts("[tab]"); break;
			case KEY_BREAK: puts("[break]"); break;
			}
		} else {
			printf("%c (%02x)\n", (char)k, k);
		}
	}
}

void shell_main(struct MemoryRange *r) {
	lua_State *L;
	char buffer[128];

	sys_ops->alloc->init(r->start, r->length);

	L = luaL_newstate();
	if (L == NULL) {
		puts("Could not allocate lua state\r\n");
		exit(EXIT_FAILURE);
	}

	luaL_openlibs(L);

	lua_pushcfunction(L, peek);
	lua_setglobal(L, "peek");
	lua_pushcfunction(L, poke);
	lua_setglobal(L, "poke");

	puts(LUA_VERSION " Okay!\n");

	while(1) {
		fputs("> ", stdout);
		fflush(stdout);

		uint32_t n = sys_ops->console->readline((uint8_t *)buffer, 127);
		buffer[n] = 0;
		puts("");

		if (strcmp(buffer, "clear") == 0) {
			sys_ops->console->clear();
		} else if (strcmp(buffer, "svcall") == 0) {
			svc64(42, 69, 88, 64);
		} else if (strcmp(buffer, "malloc") == 0) {
			char *s = sys_ops->alloc->malloc(1024);
			if (!s) {
				puts("alloc failed");
				continue;
			}
			const char hello[] = "Hello alloc!";
			for (int i = 0; i < strlen(hello); i++) {
				s[i] = hello[i];
			}
		} else if (strcmp(buffer, "dbz") == 0) {
			// Enable DIV_0_TRP
			*(uint32_t*)0xe000ed14 = 0x10;
			__asm__(
				"mov r0, #1\n"
				"mov r1, #0\n"
				"sdiv r0, r0, r1\n"
			);
		} else if (strcmp(buffer, "keydebug") == 0) {
			keydebug();
		} else {
			luaL_loadstring(L, buffer);
			int r = lua_pcall(L, 0, LUA_MULTRET, 0);
			if (r != 0) {
				fputs("error: ", stderr);
				switch(r) {
				case LUA_ERRRUN:
					break;
				case LUA_ERRMEM:
					puts("memory allocation");
					break;
				case LUA_ERRERR:
					puts("message handler");
					break;
				case LUA_ERRGCMM:
					puts("__gc metamethod");
					break;
				default:
					puts("unknown");
				}
				int top = lua_gettop(L);
				puts(luaL_tolstring(L, top, NULL));
			} else {
				int top = lua_gettop(L);
				if (top == 1) {
					puts(luaL_tolstring(L, 1, NULL));
				} else {
					for (int i = 1; i <= top; i++) {
						printf("%d: %s\n", i, luaL_tolstring(L, i, NULL));
					}
				}
			}

			lua_settop(L, 0);
		}
	}
}