aindump: improve disassembler output

Replace many instruction arguments with names/values (strings/messages,
local/global variables, structs, functions, etc.) and add labels to jump
targets.
This commit is contained in:
Nunuhara Cabbage
2020-01-01 20:34:35 -08:00
parent f272f807e1
commit 03e9d59825
8 changed files with 505 additions and 75 deletions
+2
View File
@@ -208,6 +208,7 @@ struct ain {
int32_t msgf;
int32_t nr_libraries;
struct ain_library *libraries;
int32_t nr_switches;
struct ain_switch *switches;
int32_t game_version;
int32_t nr_strings;
@@ -217,6 +218,7 @@ struct ain {
int32_t ojmp;
int nr_function_types;
struct ain_function_type *function_types;
int nr_delegates;
struct ain_function_type *delegates;
int32_t nr_global_groups;
char **global_group_names;
+42 -6
View File
@@ -24,6 +24,35 @@
#define INSTRUCTION_MAX_ARGS 4 // FIXME: determine the real maximum
enum syscall_code {
SYS_EXIT = 0x00,
SYS_GLOBAL_SAVE = 0x01,
SYS_GLOBAL_LOAD = 0x02,
SYS_LOCK_PEEK = 0x03,
SYS_UNLOCK_PEEK = 0x04,
SYS_RESET = 0x05,
SYS_OUTPUT = 0x06,
SYS_MSGBOX = 0x07,
SYS_RESUME_SAVE = 0x08,
SYS_RESUME_LOAD = 0x09,
SYS_EXISTS_FILE = 0x0A,
SYS_OPEN_WEB = 0x0B,
SYS_GET_SAVE_FOLDER_NAME = 0x0C,
SYS_GET_TIME = 0x0D,
SYS_ERROR = 0x0F,
SYS_EXISTS_SAVE_FILE = 0x10,
SYS_IS_DEBUG_MODE = 0x11,
SYS_MSGBOX_OK_CANCEL = 0x12,
SYS_GET_FUNC_STACK_NAME = 0x13,
SYS_PEEK = 0x14,
SYS_SLEEP = 0x15,
SYS_GROUP_SAVE = 0x18,
SYS_GROUP_LOAD = 0x19,
NR_SYSCALLS
};
const char * const syscalls[NR_SYSCALLS];
enum opcode
{
PUSH = 0x00,
@@ -290,12 +319,19 @@ enum opcode
};
enum instruction_argtype {
INSTR_INT = 0,
INSTR_FLO,
INSTR_PTR,
INSTR_FUN,
INSTR_STR,
INSTR_FILE
T_INT,
T_FLOAT,
T_ADDR,
T_FUNC,
T_STRING,
T_MSG,
T_LOCAL,
T_GLOBAL,
T_STRUCT,
T_SYSCALL,
T_HLL,
T_HLLFUNC,
T_FILE,
};
struct instruction {
+93 -4
View File
@@ -475,8 +475,8 @@ static bool read_tag(struct ain_reader *r, struct ain *ain)
ain->nr_libraries = read_int32(r);
ain->libraries = read_libraries(r, ain->nr_libraries);
} else if (TAG_EQ("SWI0")) {
int32_t count = read_int32(r);
ain->switches = read_switches(r, count);
ain->nr_switches = read_int32(r);
ain->switches = read_switches(r, ain->nr_switches);
} else if (TAG_EQ("GVER")) {
ain->game_version = read_int32(r);
} else if (TAG_EQ("STR0")) {
@@ -493,8 +493,8 @@ static bool read_tag(struct ain_reader *r, struct ain *ain)
ain->function_types = read_function_types(r, ain->nr_function_types, ain);
} else if (TAG_EQ("DELG")) {
read_int32(r); // ???
int32_t count = read_int32(r);
ain->delegates = read_function_types(r, count, ain);
ain->nr_delegates = read_int32(r);
ain->delegates = read_function_types(r, ain->nr_delegates, ain);
} else if (TAG_EQ("OBJG")) {
ain->nr_global_groups = read_int32(r);
ain->global_group_names = read_strings(r, ain->nr_global_groups);
@@ -655,7 +655,96 @@ err:
return NULL;
}
static void ain_free_variables(struct ain_variable *vars, int nr_vars)
{
for (int i = 0; i < nr_vars; i++) {
free(vars[i].name);
free(vars[i].name2);
}
free(vars);
}
static void ain_free_function_types(struct ain_function_type *funs, int n)
{
for (int i = 0; i < n; i++) {
free(funs[i].name);
ain_free_variables(funs[i].variables, funs[i].nr_variables);
}
free(funs);
}
static void ain_free_strings(struct string **strings, int n)
{
for (int i = 0; i < n; i++) {
free_string(strings[i]);
}
free(strings);
}
static void ain_free_cstrings(char **strings, int n)
{
for (int i = 0; i < n; i++) {
free(strings[i]);
}
free(strings);
}
void ain_free(struct ain *ain)
{
free(ain->code);
for (int f = 0; f < ain->nr_functions; f++) {
free(ain->functions[f].name);
ain_free_variables(ain->functions[f].vars, ain->functions[f].nr_vars);
}
free(ain->functions);
for (int i = 0; i < ain->nr_globals; i++) {
free(ain->globals[i].name);
free(ain->globals[i].name2);
}
free(ain->globals);
for (int i = 0; i < ain->nr_initvals; i++) {
if (ain->global_initvals[i].data_type != AIN_STRING)
continue;
free(ain->global_initvals[i].string_value);
}
free(ain->global_initvals);
for (int s = 0; s < ain->nr_structures; s++) {
free(ain->structures[s].name);
ain_free_variables(ain->structures[s].members, ain->structures[s].nr_members);
}
free(ain->structures);
for (int lib = 0; lib < ain->nr_libraries; lib++) {
free(ain->libraries[lib].name);
for (int f = 0; f < ain->libraries[lib].nr_functions; f++) {
free(ain->libraries[lib].functions[f].name);
for (int a = 0; a < ain->libraries[lib].functions[f].nr_arguments; a++) {
free(ain->libraries[lib].functions[f].arguments[a].name);
}
free(ain->libraries[lib].functions[f].arguments);
}
free(ain->libraries[lib].functions);
}
free(ain->libraries);
for (int i = 0; i < ain->nr_switches; i++) {
free(ain->switches[i].cases);
}
free(ain->switches);
ain_free_function_types(ain->function_types, ain->nr_function_types);
ain_free_function_types(ain->delegates, ain->nr_delegates);
ain_free_strings(ain->strings, ain->nr_strings);
ain_free_strings(ain->messages, ain->nr_messages);
ain_free_cstrings(ain->filenames, ain->nr_filenames);
ain_free_cstrings(ain->global_group_names, ain->nr_global_groups);
ain_free_cstrings(ain->enums, ain->nr_enums);
free(ain);
}
+5 -15
View File
@@ -26,6 +26,8 @@
#include "system4/string.h"
#include "system4/utfsjis.h"
void disassemble_ain(struct ain *ain, FILE *out);
static void usage(void)
{
puts("Usage: aindump <options> <ainfile>");
@@ -56,20 +58,6 @@ static void ain_dump_version(struct ain *ain)
printf("AIN VERSION %d\n", ain->version);
}
static void ain_dump_code(struct ain *ain)
{
for (size_t i = 0; i < ain->code_size;) {
uint16_t opcode = LittleEndian_getW(ain->code, i);
const struct instruction *instr = &instructions[opcode];
printf("%s", instr->name);
for (int j = 0; j < instr->nr_args; j++) {
printf(" 0x%x", LittleEndian_getDW(ain->code, i + 2 + j*4));
}
putchar('\n');
i += instruction_width(opcode);
}
}
static void print_arglist(struct ain *ain, struct ain_variable *args, int nr_args)
{
if (!nr_args) {
@@ -342,5 +330,7 @@ int main(int argc, char *argv[])
if (dump_enums)
ain_dump_enums(ain);
if (dump_code)
ain_dump_code(ain);
disassemble_ain(ain, stdout);
ain_free(ain);
}
+310
View File
@@ -0,0 +1,310 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include "little_endian.h"
#include "system4/ain.h"
#include "system4/instructions.h"
#include "system4/string.h"
#include "system4/utfsjis.h"
#define DASM_ERROR(dasm, fmt, ...) ERROR("At 0x%x: " fmt, dasm->addr, ##__VA_ARGS__)
struct dasm_state {
struct ain *ain;
FILE *out;
size_t addr;
int func;
};
#define NR_LABEL_BUCKETS 128
struct label {
char *name;
size_t addr;
};
struct label_bucket {
int nr_labels;
struct label *labels;
};
struct label_bucket label_table[NR_LABEL_BUCKETS];
static struct label_bucket *get_bucket(size_t addr)
{
return &label_table[(addr>>4) % NR_LABEL_BUCKETS];
}
static char *get_label(size_t addr)
{
struct label_bucket *bucket = get_bucket(addr);
for (int i = 0; i < bucket->nr_labels; i++) {
if (bucket->labels[i].addr == addr)
return bucket->labels[i].name;
}
return NULL;
}
static void add_label(char *name, size_t addr)
{
// NOTE: first registered label takes precedence
if (get_label(addr)) {
free(name);
return;
}
struct label_bucket *bucket = get_bucket(addr);
int n = bucket->nr_labels;
bucket->labels = xrealloc(bucket->labels, sizeof(struct label) * (n + 1));
bucket->nr_labels = n+1;
bucket->labels[n].name = name;
bucket->labels[n].addr = addr;
}
union float_cast {
int32_t i;
float f;
};
static float arg_to_float(int32_t arg)
{
union float_cast v;
v.i = arg;
return v.f;
}
static void print_sjis(struct dasm_state *dasm, const char *s)
{
char *u = sjis2utf(s, strlen(s));
fprintf(dasm->out, "%s", u);
free(u);
}
static void print_string(struct dasm_state *dasm, struct string *s)
{
int i, escapes = 0;
char *u = sjis2utf(s->text, s->size);
// check for characters that need escaping
for (i = 0; u[i]; i++) {
if (u[i] == '"' || u[i] == '\n') {
escapes++;
}
}
// add backslash escapes
if (escapes > 0) {
int j = 0;
char *tmp = xmalloc(i + escapes + 1);
for (int i = 0; u[i];) {
if (u[i] == '"') {
tmp[j++] = '\\';
tmp[j++] = u[i++];
} else if (u[i] == '\n') {
tmp[j++] = '\\';
tmp[j++] = 'n';
i++;
} else {
tmp[j++] = u[i++];
}
}
tmp[i+escapes] = '\0';
free(u);
u = tmp;
}
fprintf(dasm->out, "\"%s\"", u);
free(u);
}
static void print_argument(struct dasm_state *dasm, int32_t arg, enum instruction_argtype type)
{
char *label;
struct ain *ain = dasm->ain;
switch (type) {
case T_INT:
fprintf(dasm->out, "0x%x", arg);
break;
case T_FLOAT:
fprintf(dasm->out, "%f", arg_to_float(arg));
break;
case T_ADDR:
label = get_label(arg);
if (!label) {
WARNING("No label generated for address: 0x%x", arg);
fprintf(dasm->out, "0x%x", arg);
} else {
fprintf(dasm->out, "%s", label);
}
break;
case T_FUNC:
if (arg < 0 || arg >= ain->nr_functions)
DASM_ERROR(dasm, "Invalid function number: %d", arg);
print_sjis(dasm, ain->functions[arg].name);
break;
case T_STRING:
if (arg < 0 || arg >= ain->nr_strings)
DASM_ERROR(dasm, "Invalid string number: %d", arg);
print_string(dasm, ain->strings[arg]);
break;
case T_MSG:
if (arg < 0 || arg >= ain->nr_messages)
DASM_ERROR(dasm, "Invalid message number: %d", arg);
print_string(dasm, ain->messages[arg]);
break;
case T_LOCAL:
if (dasm->func < 0)
DASM_ERROR(dasm, "Attempt to access local variable outside of function");
if (arg < 0 || arg >= ain->functions[dasm->func].nr_vars)
DASM_ERROR(dasm, "Invalid variable number: %d", arg);
print_sjis(dasm, ain->functions[dasm->func].vars[arg].name);
break;
case T_GLOBAL:
if (arg < 0 || arg >= ain->nr_globals)
DASM_ERROR(dasm, "Invalid global number: %d", arg);
print_sjis(dasm, ain->globals[arg].name);
break;
case T_STRUCT:
if (arg < 0 || arg >= ain->nr_structures)
DASM_ERROR(dasm, "Invalid struct number: %d", arg);
print_sjis(dasm, ain->structures[arg].name);
break;
case T_SYSCALL:
if (arg < 0 || arg >= NR_SYSCALLS || !syscalls[arg])
DASM_ERROR(dasm, "Invalid/unknown syscall number: %d", arg);
fprintf(dasm->out, "%s", syscalls[arg]);
break;
case T_HLL:
if (arg < 0 || arg >= ain->nr_libraries)
DASM_ERROR(dasm, "Invalid HLL library number: %d", arg);
print_sjis(dasm, ain->libraries[arg].name);
break;
case T_HLLFUNC:
fprintf(dasm->out, "0x%x", arg);
break;
case T_FILE:
if (arg < 0 || arg >= ain->nr_filenames)
DASM_ERROR(dasm, "Invalid file number: %d", arg);
print_sjis(dasm, ain->filenames[arg]);
break;
default:
fprintf(dasm->out, "<UNKNOWN ARG TYPE?>");
break;
}
}
static void print_arguments(struct dasm_state *dasm, const struct instruction *instr)
{
if (instr->opcode == CALLHLL) {
int32_t lib = LittleEndian_getDW(dasm->ain->code, dasm->addr + 2);
int32_t fun = LittleEndian_getDW(dasm->ain->code, dasm->addr + 6);
fprintf(dasm->out, " %s.%s", dasm->ain->libraries[lib].name, dasm->ain->libraries[lib].functions[fun].name);
return;
}
for (int i = 0; i < instr->nr_args; i++) {
fputc(' ', dasm->out);
print_argument(dasm, LittleEndian_getDW(dasm->ain->code, dasm->addr + 2 + i*4), instr->args[i]);
}
}
static void print_instruction(struct dasm_state *dasm, const struct instruction *instr)
{
switch (instr->opcode) {
case FUNC:
dasm->func = LittleEndian_getDW(dasm->ain->code, dasm->addr + 2);
if (dasm->func < 0 || dasm->func >= dasm->ain->nr_functions)
DASM_ERROR(dasm, "Invalid function number: %d", dasm->func);
break;
case ENDFUNC:
dasm->func = -1;
break;
case _EOF:
break;
default:
fputc('\t', dasm->out);
break;
}
fprintf(dasm->out, "%s", instr->name);
print_arguments(dasm, instr);
fputc('\n', dasm->out);
}
static const struct instruction *get_instruction(struct dasm_state *dasm)
{
uint16_t opcode = LittleEndian_getW(dasm->ain->code, dasm->addr);
const struct instruction *instr = &instructions[opcode];
if (opcode >= NR_OPCODES)
DASM_ERROR(dasm, "Unknown/invalid opcode: %u", opcode);
if (dasm->addr + instr->nr_args * 4 >= dasm->ain->code_size)
DASM_ERROR(dasm, "CODE section truncated?");
return instr;
}
static char *genlabel(size_t addr)
{
char name[64];
snprintf(name, 64, "0x%lx", addr);
return strdup(name);
}
void disassemble_ain(struct ain *ain, FILE *out)
{
struct dasm_state dasm = {
.ain = ain,
.out = out,
.addr = 0,
.func = -1
};
// first pass to generate labels
for (dasm.addr = 0; dasm.addr < dasm.ain->code_size;) {
const struct instruction *instr = get_instruction(&dasm);
switch (instr->opcode) {
case JUMP:
case IFZ:
case IFNZ: {
int32_t arg = LittleEndian_getDW(dasm.ain->code, dasm.addr + 2);
add_label(genlabel(arg), arg);
break;
}
default: break;
}
dasm.addr += instruction_width(instr->opcode);
}
for (dasm.addr = 0; dasm.addr < dasm.ain->code_size;) {
const struct instruction *instr = get_instruction(&dasm);
char *label = get_label(dasm.addr);
if (label)
fprintf(dasm.out, "%s:\n", label);
print_instruction(&dasm, instr);
dasm.addr += instruction_width(instr->opcode);
}
fflush(dasm.out);
for (int i = 0; i < NR_LABEL_BUCKETS; i++) {
for (int j = 0; j < label_table[i].nr_labels; j++) {
free(label_table[i].labels[j].name);
}
free(label_table[i].labels);
}
}
+51 -25
View File
@@ -21,6 +21,32 @@
#include "system4.h"
#include "system4/instructions.h"
const char * const syscalls[NR_SYSCALLS] = {
[SYS_EXIT] = "system.Exit",
[SYS_GLOBAL_SAVE] = "system.GlobalSave",
[SYS_GLOBAL_LOAD] = "system.GlobalLoad",
[SYS_LOCK_PEEK] = "system.LockPeek",
[SYS_UNLOCK_PEEK] = "system.UnlockPeek",
[SYS_RESET] = "system.Reset",
[SYS_OUTPUT] = "system.Output",
[SYS_MSGBOX] = "system.MsgBox",
[SYS_RESUME_SAVE] = "system.ResumeSave",
[SYS_RESUME_LOAD] = "system.ResumeLoad",
[SYS_EXISTS_FILE] = "system.ExistsFile",
[SYS_OPEN_WEB] = "system.OpenWeb",
[SYS_GET_SAVE_FOLDER_NAME] = "system.GetSaveFolderName",
[SYS_GET_TIME] = "system.GetTime",
[SYS_ERROR] = "system.Error",
[SYS_EXISTS_SAVE_FILE] = "system.ExistsSaveFile",
[SYS_IS_DEBUG_MODE] = "system.IsDebugMode",
[SYS_MSGBOX_OK_CANCEL] = "system.MsgBoxOkCancel",
[SYS_GET_FUNC_STACK_NAME] = "system.GetFuncStackName",
[SYS_PEEK] = "system.Peek",
[SYS_SLEEP] = "system.Sleep",
[SYS_GROUP_SAVE] = "system.GroupSave",
[SYS_GROUP_LOAD] = "system.GroupLoad"
};
// JMP = instruction that modifies the instruction pointer
// OP = everything else
#define OP(code, nargs, ...) \
@@ -41,7 +67,7 @@
}
const struct instruction instructions[NR_OPCODES] = {
OP(PUSH, 1, INSTR_INT),
OP(PUSH, 1, T_INT),
OP(POP, 0),
OP(REF, 0),
OP(REFREF, 0),
@@ -89,11 +115,11 @@ const struct instruction instructions[NR_OPCODES] = {
OP(CMP, 0), // TODO
JMP(JUMP, 1, INSTR_PTR),
JMP(IFZ, 1, INSTR_PTR),
JMP(IFNZ, 1, INSTR_PTR),
JMP(JUMP, 1, T_ADDR),
JMP(IFZ, 1, T_ADDR),
JMP(IFNZ, 1, T_ADDR),
JMP(RETURN, 0),
JMP(CALLFUNC, 1, INSTR_FUN),
JMP(CALLFUNC, 1, T_FUNC),
OP(INC, 0),
OP(DEC, 0),
OP(FTOI, 0),
@@ -109,8 +135,8 @@ const struct instruction instructions[NR_OPCODES] = {
OP(F_GTE, 0),
OP(F_NOTE, 0),
OP(F_EQUALE, 0),
OP(F_PUSH, 1, INSTR_FLO),
OP(S_PUSH, 1, INSTR_STR),
OP(F_PUSH, 1, T_FLOAT),
OP(S_PUSH, 1, T_STRING),
OP(S_POP, 0),
OP(S_ADD, 0),
OP(S_ASSIGN, 0),
@@ -125,7 +151,7 @@ const struct instruction instructions[NR_OPCODES] = {
OP(SR_POP, 0),
OP(SR_ASSIGN, 0),
OP(SR_REF, 1, INSTR_INT),
OP(SR_REF, 1, T_STRUCT),
OP(SR_REFREF, 0), // TODO
OP(A_ALLOC, 0),
OP(A_REALLOC, 0),
@@ -135,21 +161,21 @@ const struct instruction instructions[NR_OPCODES] = {
OP(A_FILL, 0),
OP(C_REF, 0),
OP(C_ASSIGN, 0),
JMP(MSG, 1, INSTR_INT),
OP(CALLHLL, 2, INSTR_INT, INSTR_INT),
JMP(MSG, 1, T_MSG),
OP(CALLHLL, 2, T_HLL, T_HLLFUNC),
OP(PUSHSTRUCTPAGE, 0),
JMP(CALLMETHOD, 1, INSTR_FUN),
OP(SH_GLOBALREF, 1, INSTR_INT),
OP(SH_LOCALREF, 1, INSTR_INT),
JMP(SWITCH, 1),
JMP(STRSWITCH, 1),
OP(FUNC, 1, INSTR_FUN),
OP(_EOF, 1, INSTR_FILE),
OP(CALLSYS, 1, INSTR_INT),
JMP(CALLMETHOD, 1, T_FUNC),
OP(SH_GLOBALREF, 1, T_GLOBAL),
OP(SH_LOCALREF, 1, T_LOCAL),
JMP(SWITCH, 1, T_INT),
JMP(STRSWITCH, 1, T_INT),
OP(FUNC, 1, T_FUNC),
OP(_EOF, 1, T_FILE),
OP(CALLSYS, 1, T_SYSCALL),
JMP(SJUMP, 0),
OP(CALLONJUMP, 0),
OP(SWAP, 0),
OP(SH_STRUCTREF, 1, INSTR_INT),
OP(SH_STRUCTREF, 1, T_INT),
OP(S_LENGTH, 0),
OP(S_LENGTHBYTE, 0),
OP(I_STRING, 0),
@@ -172,11 +198,11 @@ const struct instruction instructions[NR_OPCODES] = {
OP(DUP_U2, 0),
OP(SP_INC, 0),
OP(SP_DEC, 0), // TODO
OP(ENDFUNC, 1, INSTR_FUN),
OP(ENDFUNC, 1, T_FUNC),
OP(R_EQUALE, 0), // TODO
OP(R_NOTE, 0), // TODO
OP(SH_LOCALCREATE, 2),
OP(SH_LOCALDELETE, 1),
OP(SH_LOCALCREATE, 2, T_LOCAL, T_STRUCT),
OP(SH_LOCALDELETE, 1, T_LOCAL),
OP(STOI, 0), // TODO
OP(A_PUSHBACK, 0),
OP(A_POPBACK, 0),
@@ -184,9 +210,9 @@ const struct instruction instructions[NR_OPCODES] = {
OP(A_EMPTY, 0),
OP(A_ERASE, 0),
OP(A_INSERT, 0),
OP(SH_LOCALINC, 1, INSTR_INT),
OP(SH_LOCALDEC, 1, INSTR_INT),
OP(SH_LOCALASSIGN, 2, INSTR_INT, INSTR_INT),
OP(SH_LOCALINC, 1, T_LOCAL),
OP(SH_LOCALDEC, 1, T_LOCAL),
OP(SH_LOCALASSIGN, 2, T_LOCAL, T_INT),
OP(ITOB, 0),
OP(S_FIND, 0),
OP(S_GETPART, 0),
+1 -1
View File
@@ -62,7 +62,7 @@ executable('xsystem4', xsystem4,
include_directories : incdir,
link_with : libsys4)
executable('aindump', ['aindump.c'],
executable('aindump', ['aindump/aindump.c', 'aindump/dasm.c'],
dependencies : [libm, zlib],
include_directories : incdir,
link_with : libsys4)
+1 -24
View File
@@ -354,30 +354,7 @@ static void function_return(void)
call_stack_ptr--;
}
enum syscall_code {
SYS_EXIT = 0x00,
SYS_GLOBAL_SAVE = 0x01,
SYS_GLOBAL_LOAD = 0x02,
SYS_LOCK_PEEK = 0x03,
SYS_UNLOCK_PEEK = 0x04,
SYS_OUTPUT = 0x06,
SYS_MSGBOX = 0x07,
SYS_RESUME_SAVE = 0x08,
SYS_RESUME_LOAD = 0x09,
SYS_EXISTS_FILE = 0x0A,
SYS_GET_SAVE_FOLDER_NAME = 0x0C,
SYS_GET_TIME = 0x0D,
SYS_ERROR = 0x0F,
SYS_EXISTS_SAVE_FILE = 0x10,
SYS_IS_DEBUG_MODE = 0x11,
SYS_GET_FUNC_STACK_NAME = 0x13,
SYS_PEEK = 0x14,
SYS_SLEEP = 0x15,
SYS_GROUP_SAVE = 0x18,
SYS_GROUP_LOAD = 0x19
};
static void system_call(int32_t code)
static void system_call(enum syscall_code code)
{
switch (code) {
case SYS_EXIT: {// system.Exit(int nResult)