commit 84df756fafb18da3ecb4526155e4326640284aba Author: Nunuhara Cabbage Date: Sun Oct 6 18:26:59 2019 -0700 Initial commit An assortment of instructions have been implemented so far, mostly basic stuff such as stack management. The following program can be executed: int main(void) { hello(2); for (;;) { system.Peek(); system.Sleep(1); } return 0; } void hello(int n) { int i; for (i = 0; i < n; i++) { system.Output("Hello, world! " + string(i)); } } diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..364ff39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +*.ain \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc656ac --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +xsystem4 +======== + +xsystem4 is an implementation of AliceSoft's System 4 game engine for unix-like +operating systems. + +NOTE: This is a work in progress. Very little is implemented so far. + +Building +-------- + +xsystem4 uses the meson build system. + + mkdir build + meson build + ninja -C build + +Running +------- + +You will need to create an AIN file using the System 4 SDK compiler. Then simply +pass it to the system4 executable. + + build/system4 ???.ain diff --git a/ain.c b/ain.c new file mode 100644 index 0000000..8fac4fa --- /dev/null +++ b/ain.c @@ -0,0 +1,445 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * Credit to SLC for reverse engineering AIN formats. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "system4.h" +#include "ain.h" +#include "little_endian.h" +#include "instructions.h" + +static const char *errtab[AIN_MAX_ERROR] = { + [AIN_SUCCESS] = "Success", + [AIN_FILE_ERROR] = "Error opening AIN file", + [AIN_UNRECOGNIZED_FORMAT] = "Unrecognized or invalid AIN format", + [AIN_INVALID] = "Invalid AIN file" +}; + +const char *ain_strerror(int error) +{ + if (error < AIN_MAX_ERROR) + return errtab[error]; + return "Invalid error code"; +} + +struct ain_reader { + uint8_t *buf; + size_t size; + size_t index; +}; + +static int32_t read_int32(struct ain_reader *r) +{ + int32_t v = LittleEndian_getDW(r->buf, r->index); + r->index += 4; + return v; +} + +static uint8_t *read_bytes(struct ain_reader *r, size_t len) +{ + uint8_t *bytes = xmalloc(len); + memcpy(bytes, r->buf + r->index, len); + r->index += len; + return bytes; +} + +static char *read_string(struct ain_reader *r) +{ + char *str = strdup((char*)r->buf + r->index); + r->index += strlen(str) + 1; + return str; +} + +// TODO: should just memcpy the whole section and create pointers into it +// e.g. char *_messages; // raw AIN section +// char **messages; // array of pointers into _messages +static char **read_strings(struct ain_reader *r, int count) +{ + char **strings = calloc(count, sizeof(char*)); + for (int i = 0; i < count; i++) { + strings[i] = read_string(r); + } + return strings; +} + +static struct ain_variable *read_variables(struct ain_reader *r, int count) +{ + struct ain_variable *variables = calloc(count, sizeof(struct ain_variable)); + for (int i = 0; i < count; i++) { + variables[i].name = read_string(r); + variables[i].data_type = read_int32(r); + variables[i].struct_type = read_int32(r); + variables[i].array_dimensions = read_int32(r); + } + return variables; +} + +static struct ain_function *read_functions(struct ain_reader *r, int count, struct ain *ain) +{ + struct ain_function *funs = calloc(count, sizeof(struct ain_function)); + for (int i = 0; i < count; i++) { + funs[i].address = read_int32(r); + funs[i].name = read_string(r); + if (ain->version > 0 && ain->version < 7) { + funs[i].is_label = read_int32(r); + } + funs[i].data_type = read_int32(r); + funs[i].struct_type = read_int32(r); + funs[i].nr_args = read_int32(r); + funs[i].nr_vars = read_int32(r); + if (ain->version > 0) { + funs[i].crc = read_int32(r); + } + if (funs[i].nr_vars > 0) { + funs[i].vars = read_variables(r, funs[i].nr_vars); + } + } + return funs; +} + +static struct ain_global *read_globals(struct ain_reader *r, int count, struct ain *ain) +{ + struct ain_global *globals = calloc(count, sizeof(struct ain_global)); + for (int i = 0; i < count; i++) { + globals[i].name = read_string(r); + globals[i].data_type = read_int32(r); + globals[i].struct_type = read_int32(r); + globals[i].array_dimensions = read_int32(r); + if (ain->version >= 5) { + globals[i].group_index = read_int32(r); + } + } + return globals; +} + +static struct ain_initval *read_initvals(struct ain_reader *r, int count) +{ + struct ain_initval *values = calloc(count, sizeof(struct ain_initval)); + for (int i = 0; i < count; i++) { + values[i].global_index = read_int32(r); + values[i].data_type = read_int32(r); + if (values[i].data_type == AIN_STRING) { + values[i].string_value = read_string(r); + } else { + values[i].int_value = read_int32(r); + } + } + return values; +} + +static struct ain_struct *read_structures(struct ain_reader *r, int count) +{ + struct ain_struct *structures = calloc(count, sizeof(struct ain_struct)); + for (int i = 0; i < count; i++) { + structures[i].name = read_string(r); + structures[i].constructor = read_int32(r); + structures[i].destructor = read_int32(r); + structures[i].nr_members = read_int32(r); + structures[i].members = read_variables(r, structures[i].nr_members); + } + return structures; +} + +static struct ain_hll_argument *read_hll_arguments(struct ain_reader *r, int count) +{ + struct ain_hll_argument *arguments = calloc(count, sizeof(struct ain_hll_argument)); + for (int i = 0; i < count; i++) { + arguments[i].name = read_string(r); + arguments[i].data_type = read_int32(r); + } + return arguments; +} + +static struct ain_hll_function *read_hll_functions(struct ain_reader *r, int count) +{ + struct ain_hll_function *functions = calloc(count, sizeof(struct ain_hll_function)); + for (int i = 0; i < count; i++) { + functions[i].name = read_string(r); + functions[i].data_type = read_int32(r); + functions[i].nr_arguments = read_int32(r); + functions[i].arguments = read_hll_arguments(r, functions[i].nr_arguments); + } + return functions; +} + +static struct ain_library *read_libraries(struct ain_reader *r, int count) +{ + struct ain_library *libraries = calloc(count, sizeof(struct ain_library)); + for (int i = 0; i < count; i++) { + libraries[i].name = read_string(r); + libraries[i].nr_functions = read_int32(r); + libraries[i].functions = read_hll_functions(r, libraries[i].nr_functions); + } + return libraries; +} + +static struct ain_switch_case *read_switch_cases(struct ain_reader *r, int count) +{ + struct ain_switch_case *cases = calloc(count, sizeof(struct ain_switch)); + for (int i = 0; i < count; i++) { + cases[i].value = read_int32(r); + cases[i].address = read_int32(r); + } + return cases; +} + +static struct ain_switch *read_switches(struct ain_reader *r, int count) +{ + struct ain_switch *switches = calloc(count, sizeof(struct ain_switch)); + for (int i = 0; i < count; i++) { + switches[i].case_type = read_int32(r); + switches[i].default_address = read_int32(r); + switches[i].nr_cases = read_int32(r); + switches[i].cases = read_switch_cases(r, switches[i].nr_cases); + } + return switches; +} + +static struct ain_function_type *read_function_types(struct ain_reader *r, int count) +{ + struct ain_function_type *types = calloc(count, sizeof(struct ain_function_type)); + for (int i = 0; i < count; i++) { + types[i].name = read_string(r); + types[i].data_type = read_int32(r); + types[i].struct_type = read_int32(r); + types[i].nr_arguments = read_int32(r); + types[i].nr_variables = read_int32(r); + types[i].variables = read_variables(r, types[i].nr_variables); + } + return types; +} + +static bool read_tag(struct ain_reader *r, struct ain *ain) +{ + uint8_t *tag_loc = r->buf + r->index; + r->index += 4; + +#define TAG_EQ(tag) !strncmp((char*)tag_loc, tag, 4) + // FIXME: need to check len or could segfault on currupt AIN file + if (TAG_EQ("VERS")) { + ain->version = read_int32(r); + } else if (TAG_EQ("KEYC")) { + ain->keycode = read_int32(r); + } else if (TAG_EQ("CODE")) { + ain->code_size = read_int32(r); + ain->code = read_bytes(r, ain->code_size); + } else if (TAG_EQ("FUNC")) { + int32_t count = read_int32(r); + ain->functions = read_functions(r, count, ain); + } else if (TAG_EQ("GLOB")) { + int32_t count = read_int32(r); + ain->globals = read_globals(r, count, ain); + } else if (TAG_EQ("GSET")) { + int32_t count = read_int32(r); + ain->global_initvals = read_initvals(r, count); + } else if (TAG_EQ("STRT")) { + int32_t count = read_int32(r); + ain->structures = read_structures(r, count); + } else if (TAG_EQ("MSG0")) { + ain->messages = read_strings(r, read_int32(r)); + } else if (TAG_EQ("MAIN")) { + ain->main = read_int32(r); + } else if (TAG_EQ("MSGF")) { + ain->msgf = read_int32(r); + } else if (TAG_EQ("HLL0")) { + int32_t count = read_int32(r); + ain->libraries = read_libraries(r, count); + } else if (TAG_EQ("SWI0")) { + int32_t count = read_int32(r); + ain->switches = read_switches(r, count); + } else if (TAG_EQ("GVER")) { + ain->game_version = read_int32(r); + } else if (TAG_EQ("STR0")) { + ain->strings = read_strings(r, read_int32(r)); + } else if (TAG_EQ("FNAM")) { + ain->filenames = read_strings(r, read_int32(r)); + } else if (TAG_EQ("OJMP")) { + ain->ojmp = read_int32(r); + } else if (TAG_EQ("FNCT")) { + //int32_t length = read_int32(r) - 4; // ??? + read_int32(r); + int32_t count = read_int32(r); + ain->function_types = read_function_types(r, count); + //} else if (TAG_EQ("DELG")) { + } else if (TAG_EQ("OBJG")) { + ain->global_group_names = read_strings(r, read_int32(r)); + //} else if (TAG_EQ("MSG1")) { + } else { + return false; + } +#undef TAG_EQ + + return true; +} + +static uint8_t *decompress_ain(uint8_t *in, long *len) +{ + uint8_t *out; + long out_len = LittleEndian_getDW(in, 8); + long in_len = LittleEndian_getDW(in, 12); + + if (out_len < 0 || in_len < 0) + return NULL; + + out = xmalloc(out_len); + if (Z_OK != uncompress(out, (unsigned long*)&out_len, in+16, in_len)) { + free(out); + return NULL; + } + + *len = out_len; + return out; +} + +static uint32_t temper(uint32_t in) +{ + in ^= (in >> 11); + in ^= (in << 7) & 0x9D2C5680; + in ^= (in << 15) & 0xEFC60000; + in ^= (in >> 18); + return in; +} + +static void update(uint32_t *state) +{ + int ahead = 0x18D; + int write = 0; + int read = 2; + uint32_t current = state[1]; + uint32_t last = state[0]; + uint32_t tmp; + + for (int i = 0; i < 0x270; i++) { + tmp = (((current ^ last) & 0x7FFFFFFE) ^ last) >> 1; + tmp = (current & 1) ? (tmp ^ 0x9908B0DF) : tmp; + state[write] = tmp ^ state[ahead]; + last = current; + current = state[read]; + write++; + read++; + ahead++; + while (read >= 0x270) + read -= 0x270; + while (ahead >= 0x270) + ahead -= 0x270; + } +} + +static void decrypt_ain(uint8_t *buf, size_t len) +{ + uint32_t state[0x270]; + uint32_t key = 0x5D3E3; + int off = 0; + + for (int i = 0; i < 0x270; i++) { + state[i] = key; + key *= 0x10DCD; + } + + update(state); + for (size_t i = 0; i < len; i++) { + if (off >= 0x270) { + update(state); + off = 0; + } + buf[i] ^= (uint8_t)(temper(state[off]) & 0xFF); + off++; + } +} + +static bool ain_is_encrypted(uint8_t *buf) +{ + uint8_t magic[8]; + + memcpy(magic, buf, 8); + decrypt_ain(magic, 8); + + return !strncmp((char*)magic, "VERS", 4) && !magic[5] && !magic[6] && !magic[7]; +} + +struct ain *ain_open(const char *path, int *error) +{ + FILE *fp; + long len; + uint8_t *buf = NULL; + struct ain *ain = NULL; + + if (!(fp = fopen(path, "rb"))) { + *error = AIN_FILE_ERROR; + goto err; + } + + // get size of AIN file + fseek(fp, 0, SEEK_END); + len = ftell(fp); + fseek(fp, 0, SEEK_SET); + + // read AIN file into memory + buf = xmalloc(len + 4); // why +4? + fread(buf, 1, len, fp); + fclose(fp); + + // check magic + if (!strncmp((char*)buf, "AI2\0\0\0\0", 8)) { + uint8_t *uc = decompress_ain(buf, &len); + if (!uc) { + *error = AIN_INVALID; + goto err; + } + free(buf); + buf = uc; + } else if (ain_is_encrypted(buf)) { + decrypt_ain(buf, len); + } else { + printf("%.4s\n", buf); + *error = AIN_UNRECOGNIZED_FORMAT; + goto err; + } + + // read data into ain struct + struct ain_reader r = { + .buf = buf, + .index = 0, + .size = len + }; + ain = calloc(1, sizeof(struct ain)); + while (read_tag(&r, ain)); + if (!ain->version) { + *error = AIN_INVALID; + goto err; + } + + free(buf); + *error = AIN_SUCCESS; + return ain; +err: + free(buf); + free(ain); + return NULL; +} + +void ain_free(struct ain *ain) +{ + free(ain); +} diff --git a/ain.h b/ain.h new file mode 100644 index 0000000..e7cf2bc --- /dev/null +++ b/ain.h @@ -0,0 +1,182 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#ifndef SYSTEM4_AIN_H +#define SYSTEM4_AIN_H + +#include +#include +#include + +struct ain; + +enum ain_error { + AIN_SUCCESS, + AIN_FILE_ERROR, + AIN_UNRECOGNIZED_FORMAT, + AIN_INVALID, + AIN_MAX_ERROR +}; + +enum ain_data_type { + AIN_VOID = 0, + AIN_INT = 10, + AIN_FLOAT = 11, + AIN_STRING = 12, + AIN_STRUCT = 13, + AIN_ARRAY_INT = 14, + AIN_ARRAY_FLOAT = 15, + AIN_ARRAY_STRING = 16, + AIN_ARRAY_STRUCT = 17, + AIN_REF_INT = 18, + AIN_REF_FLOAT = 19, + AIN_REF_STRING = 20, + AIN_REF_STRUCT = 21, + AIN_REF_ARRAY_INT = 22, + AIN_REF_ARRAY_FLOAT = 23, + AIN_REF_ARRAY_STRING = 24, + AIN_REF_ARRAY_STRUCT = 25, + AIN_IMAIN_SYSTEM = 26, + AIN_FUNC_TYPE = 27, + AIN_ARRAY_FUNC_TYPE = 30, + AIN_REF_FUNC_TYPE = 31, + AIN_REF_ARRAY_FUNC_TYPE = 32, + AIN_BOOL = 47, + AIN_ARRAY_BOOL = 50, + AIN_REF_BOOL = 51, + AIN_REF_ARRAY_BOOL = 52, + AIN_LONG_INT = 55, + AIN_ARRAY_LONG_INT = 58, + AIN_REF_LONG_INT = 59, + AIN_REF_ARRAY_LONG_INT = 60, + AIN_DELEGATE = 63, + AIN_ARRAY_DELEGATE = 66, + AIN_REF_ARRAY_DELEGATE = 69 +}; + +struct ain_variable { + char *name; + int32_t data_type; + int32_t struct_type; + int32_t array_dimensions; +}; + +struct ain_function { + int32_t address; + char *name; + bool is_label; + int32_t data_type; + int32_t struct_type; + int32_t nr_args; + int32_t nr_vars; + int32_t crc; + struct ain_variable *vars; +}; + +struct ain_global { + char *name; + int32_t data_type; + int32_t struct_type; + int32_t array_dimensions; + int32_t group_index; +}; + +struct ain_initval { + int32_t global_index; + int32_t data_type; + union { + char *string_value; + int32_t int_value; + }; +}; + +struct ain_struct { + char *name; + int32_t constructor; + int32_t destructor; + int32_t nr_members; + struct ain_variable *members; +}; + +struct ain_hll_argument { + char *name; + int32_t data_type; +}; + +struct ain_hll_function { + char *name; + int32_t data_type; + int32_t nr_arguments; + struct ain_hll_argument *arguments; +}; + +struct ain_library { + char *name; + int32_t nr_functions; + struct ain_hll_function *functions; +}; + +struct ain_switch_case { + int32_t value; + int32_t address; +}; + +struct ain_switch { + int32_t case_type; + int32_t default_address; + int32_t nr_cases; + struct ain_switch_case *cases; +}; + +struct ain_function_type { + char *name; + int32_t data_type; + int32_t struct_type; + int32_t nr_arguments; + int32_t nr_variables; + struct ain_variable *variables; +}; + +struct ain { + char *ain_path; + int32_t version; + int32_t keycode; + uint8_t *code; + size_t code_size; + struct ain_function *functions; + struct ain_global *globals; + struct ain_initval *global_initvals; + struct ain_struct *structures; + char **messages; + int32_t main; + int32_t msgf; + struct ain_library *libraries; + struct ain_switch *switches; + int32_t game_version; + char **strings; + char **filenames; + int32_t ojmp; + struct ain_function_type *function_types; + //delegates + char **global_group_names; +}; + +const char *ain_strerror(int error); +struct ain *ain_open(const char *path, int *error); +void ain_free(struct ain *ain); +void vm_execute(struct ain *program); + +#endif /* SYSTEM4_AIN_H */ diff --git a/instructions.c b/instructions.c new file mode 100644 index 0000000..e4bb48e --- /dev/null +++ b/instructions.c @@ -0,0 +1,293 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#include +#include +#include + +#include "system4.h" +#include "instructions.h" + +// JMP = instruction that modifies the instruction pointer +// OP = everything else +#define OP(code, args) [code] = { .opcode = code, .name = #code , .nr_args = args, .ip_inc = 2 + args * 4 } +#define JMP(code, args) [code] = { .opcode = code, .name = #code , .nr_args = args, .ip_inc = 0 } + +const struct instruction instructions[NR_OPCODES] = { + OP(PUSH, 1), + OP(POP, 0), + OP(REF, 0), + OP(REFREF, 0), + OP(PUSHGLOBALPAGE, 0), + OP(PUSHLOCALPAGE, 0), + OP(INV, 0), + OP(NOT, 0), + OP(COMPL, 0), + OP(ADD, 0), + OP(SUB, 0), + OP(MUL, 0), + OP(DIV, 0), + OP(MOD, 0), + OP(AND, 0), + OP(OR, 0), + OP(XOR, 0), + OP(LSHIFT, 0), + OP(RSHIFT, 0), + OP(LT, 0), + OP(GT, 0), + OP(LTE, 0), + OP(GTE, 0), + OP(NOTE, 0), + OP(EQUALE, 0), + OP(ASSIGN, 0), + OP(PLUSA, 0), + OP(MINUSA, 0), + OP(MULA, 0), + OP(DIVA, 0), + OP(MODA, 0), + OP(ANDA, 0), + OP(ORA, 0), + OP(XORA, 0), + + OP(LSHIFTA, 0), + OP(RSHIFTA, 0), + + OP(F_ASSIGN, 0), + OP(F_PLUSA, 0), + OP(F_MINUSA, 0), + OP(F_MULA, 0), + OP(F_DIVA, 0), + OP(DUP2, 0), + OP(DUP_X2, 0), + + OP(CMP, 0), + + JMP(JUMP, 1), + JMP(IFZ, 1), + JMP(IFNZ, 1), + JMP(RETURN, 0), + JMP(CALLFUNC, 1), + OP(INC, 0), + OP(DEC, 0), + OP(FTOI, 0), + OP(ITOF, 0), + OP(F_INV, 0), + OP(F_ADD, 0), + OP(F_SUB, 0), + OP(F_MUL, 0), + OP(F_DIV, 0), + OP(F_LT, 0), + OP(F_GT, 0), + OP(F_LTE, 0), + OP(F_GTE, 0), + OP(F_NOTE, 0), + OP(F_EQUALE, 0), + OP(F_PUSH, 0), + OP(S_PUSH, 1), + OP(S_POP, 0), + OP(S_ADD, 0), + OP(S_ASSIGN, 0), + OP(S_PLUSA, 0), + OP(S_REF, 0), + OP(S_REFREF, 0), + OP(S_NOTE, 0), + OP(S_EQUALE, 0), + OP(SF_CREATE, 0), + OP(SF_CREATEPIXEL, 0), + OP(SF_CREATEALPHA, 0), + + OP(SR_POP, 0), + OP(SR_ASSIGN, 0), + OP(SR_REF, 0), + OP(SR_REFREF, 0), + OP(A_ALLOC, 0), + OP(A_REALLOC, 0), + OP(A_FREE, 0), + OP(A_NUMOF, 0), + OP(A_COPY, 0), + OP(A_FILL, 0), + OP(C_REF, 0), + OP(C_ASSIGN, 0), + OP(MSG, 0), + OP(CALLHLL, 0), + OP(PUSHSTRUCTPAGE, 0), + OP(CALLMETHOD, 0), + OP(SH_GLOBALREF, 1), + OP(SH_LOCALREF, 1), + OP(SWITCH, 0), + OP(STRSWITCH, 0), + OP(FUNC, 1), + OP(_EOF, 1), + OP(CALLSYS, 1), + OP(SJUMP, 0), + OP(CALLONJUMP, 0), + OP(SWAP, 0), + OP(SH_STRUCTREF, 0), + OP(S_LENGTH, 0), + OP(S_LENGTHBYTE, 0), + OP(I_STRING, 0), + OP(CALLFUNC2, 0), + OP(DUP2_X1, 0), + OP(R_ASSIGN, 0), + OP(FT_ASSIGNS, 0), + OP(ASSERT, 0), + OP(S_LT, 0), + OP(S_GT, 0), + OP(S_LTE, 0), + OP(S_GTE, 0), + OP(S_LENGTH2, 0), + OP(S_LENGTHBYTE2, 0), + OP(NEW, 0), + OP(DELETE, 0), + OP(CHECKUDO, 0), + OP(A_REF, 0), + OP(DUP, 0), + OP(DUP_U2, 0), + OP(SP_INC, 0), + OP(SP_DEC, 0), + OP(ENDFUNC, 1), + OP(R_EQUALE, 0), + OP(R_NOTE, 0), + OP(SH_LOCALCREATE, 0), + OP(SH_LOCALDELETE, 0), + OP(STOI, 0), + OP(A_PUSHBACK, 0), + OP(A_POPBACK, 0), + OP(S_EMPTY, 0), + OP(A_EMPTY, 0), + OP(A_ERASE, 0), + OP(A_INSERT, 0), + OP(SH_LOCALINC, 1), + OP(SH_LOCALDEC, 1), + OP(SH_LOCALASSIGN, 2), + OP(ITOB, 0), + OP(S_FIND, 0), + OP(S_GETPART, 0), + OP(A_SORT, 0), + OP(S_PUSHBACK, 0), + OP(S_POPBACK, 0), + OP(FTOS, 0), + OP(S_MOD, 0), + OP(S_PLUSA2, 0), + OP(OBJSWAP, 0), + OP(S_ERASE, 0), + OP(SR_REF2, 0), + OP(S_ERASE2, 0), + OP(S_PUSHBACK2, 0), + OP(S_POPBACK2, 0), + OP(ITOLI, 0), + OP(LI_ADD, 0), + OP(LI_SUB, 0), + OP(LI_MUL, 0), + OP(LI_DIV, 0), + OP(LI_MOD, 0), + OP(LI_ASSIGN, 0), + OP(LI_PLUSA, 0), + OP(LI_MINUSA, 0), + OP(LI_MULA, 0), + OP(LI_DIVA, 0), + OP(LI_MODA, 0), + OP(LI_ANDA, 0), + OP(LI_ORA, 0), + OP(LI_XORA, 0), + OP(LI_LSHIFTA, 0), + OP(LI_RSHIFTA, 0), + OP(LI_INC, 0), + OP(LI_DEC, 0), + OP(A_FIND, 0), + OP(A_REVERSE, 0), + OP(SH_SR_ASSIGN, 0), + OP(SH_MEM_ASSIGN_LOCAL, 0), + OP(A_NUMOF_GLOB_1, 0), + OP(A_NUMOF_STRUCT_1, 0), + OP(SH_MEM_ASSIGN_IMM, 0), + OP(SH_LOCALREFREF, 0), + OP(SH_LOCALASSIGN_SUB_IMM, 0), + OP(SH_IF_LOC_LT_IMM, 0), + OP(SH_IF_LOC_GE_IMM, 0), + OP(SH_LOCREF_ASSIGN_MEM, 0), + OP(PAGE_REF, 0), + OP(SH_GLOBAL_ASSIGN_LOCAL, 0), + OP(SH_STRUCTREF_GT_IMM, 0), + OP(SH_STRUCT_ASSIGN_LOCALREF_ITOB, 0), + OP(SH_LOCAL_ASSIGN_STRUCTREF, 0), + OP(SH_IF_STRUCTREF_NE_LOCALREF, 0), + OP(SH_IF_STRUCTREF_GT_IMM, 0), + OP(SH_STRUCTREF_CALLMETHOD_NO_PARAM, 0), + OP(SH_STRUCTREF2, 0), + OP(SH_REF_STRUCTREF2, 0), + OP(SH_STRUCTREF3, 0), + OP(SH_STRUCTREF2_CALLMETHOD_NO_PARAM, 0), + OP(SH_IF_STRUCTREF_Z, 0), + OP(SH_IF_STRUCT_A_NOT_EMPTY, 0), + OP(SH_IF_LOC_GT_IMM, 0), + OP(SH_IF_STRUCTREF_NE_IMM, 0), + OP(THISCALLMETHOD_NOPARAM, 0), + OP(SH_IF_LOC_NE_IMM, 0), + OP(SH_IF_STRUCTREF_EQ_IMM, 0), + OP(SH_GLOBAL_ASSIGN_IMM, 0), + OP(SH_LOCALSTRUCT_ASSIGN_IMM, 0), + OP(SH_STRUCT_A_PUSHBACK_LOCAL_STRUCT, 0), + OP(SH_GLOBAL_A_PUSHBACK_LOCAL_STRUCT, 0), + OP(SH_LOCAL_A_PUSHBACK_LOCAL_STRUCT, 0), + OP(SH_IF_SREF_NE_STR0, 0), + OP(SH_S_ASSIGN_REF, 0), + OP(SH_A_FIND_SREF, 0), + OP(SH_SREF_EMPTY, 0), + OP(SH_STRUCTSREF_EQ_LOCALSREF, 0), + OP(SH_LOCALSREF_EQ_STR0, 0), + OP(SH_STRUCTSREF_NE_LOCALSREF, 0), + OP(SH_LOCALSREF_NE_STR0, 0), + OP(SH_STRUCT_SR_REF, 0), + OP(SH_STRUCT_S_REF, 0), + OP(S_REF2, 0), + OP(SH_REF_LOCAL_ASSIGN_STRUCTREF2, 0), + OP(SH_GLOBAL_S_REF, 0), + OP(SH_LOCAL_S_REF, 0), + OP(SH_LOCALREF_SASSIGN_LOCALSREF, 0), + OP(SH_LOCAL_APUSHBACK_LOCALSREF, 0), + OP(SH_S_ASSIGN_CALLSYS19, 0), + OP(SH_S_ASSIGN_STR0, 0), + OP(SH_SASSIGN_LOCALSREF, 0), + OP(SH_STRUCTREF_SASSIGN_LOCALSREF, 0), + OP(SH_LOCALSREF_EMPTY, 0), + OP(SH_GLOBAL_APUSHBACK_LOCALSREF, 0), + OP(SH_STRUCT_APUSHBACK_LOCALSREF, 0), + OP(SH_STRUCTSREF_EMPTY, 0), + OP(SH_GLOBALSREF_EMPTY, 0), + OP(SH_SASSIGN_STRUCTSREF, 0), + OP(SH_SASSIGN_GLOBALSREF, 0), + OP(SH_STRUCTSREF_NE_STR0, 0), + OP(SH_GLOBALSREF_NE_STR0, 0), + OP(SH_LOC_LT_IMM_OR_LOC_GE_IMM, 0), + OP(A_SORT_MEM, 0), + OP(DG_ADD, 0), + OP(DG_SET, 0), + OP(DG_CALL, 0), + OP(DG_NUMOF, 0), + OP(DG_EXIST, 0), + OP(DG_ERASE, 0), + OP(DG_CLEAR, 0), + OP(DG_COPY, 0), + OP(DG_ASSIGN, 0), + OP(DG_PLUSA, 0), + OP(DG_POP, 0), + OP(DG_NEW_FROM_METHOD, 0), + OP(DG_MINUSA, 0), + OP(DG_CALLBEGIN, 0), + OP(DG_NEW, 0), + OP(DG_STR_TO_METHOD, 0), +}; diff --git a/instructions.h b/instructions.h new file mode 100644 index 0000000..2f2db92 --- /dev/null +++ b/instructions.h @@ -0,0 +1,304 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * Credit to SLC for reverse engineering the opcodes. + * + * 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 . + */ + +#ifndef SYSTEM4_INSTRUCTIONS_H +#define SYSTEM4_INSTRUCTIONS_H + +#include +#include "system4.h" + +enum opcode +{ + PUSH = 0x00, + POP = 0x01, + REF = 0x02, + REFREF = 0x03, + PUSHGLOBALPAGE = 0x04, + PUSHLOCALPAGE = 0x05, + INV = 0x06, + NOT = 0x07, + COMPL = 0x08, + ADD = 0x09, + SUB = 0x0a, + MUL = 0x0b, + DIV = 0x0c, + MOD = 0x0d, + AND = 0x0e, + OR = 0x0f, + XOR = 0x10, + LSHIFT = 0x11, + RSHIFT = 0x12, + LT = 0x13, + GT = 0x14, + LTE = 0x15, + GTE = 0x16, + NOTE = 0x17, + EQUALE = 0x18, + ASSIGN = 0x19, + PLUSA = 0x1a, + MINUSA = 0x1b, + MULA = 0x1c, + DIVA = 0x1d, + MODA = 0x1e, + ANDA = 0x1f, + ORA = 0x20, + XORA = 0x21, + LSHIFTA = 0x22, + RSHIFTA = 0x23, + F_ASSIGN = 0x24, + F_PLUSA = 0x25, + F_MINUSA = 0x26, + F_MULA = 0x27, + F_DIVA = 0x28, + DUP2 = 0x29, + DUP_X2 = 0x2a, + CMP = 0x2b, + JUMP = 0x2c, + IFZ = 0x2d, + IFNZ = 0x2e, + RETURN = 0x2f, + CALLFUNC = 0x30, + INC = 0x31, + DEC = 0x32, + FTOI = 0x33, + ITOF = 0x34, + F_INV = 0x35, + F_ADD = 0x36, + F_SUB = 0x37, + F_MUL = 0x38, + F_DIV = 0x39, + F_LT = 0x3a, + F_GT = 0x3b, + F_LTE = 0x3c, + F_GTE = 0x3D, + F_NOTE = 0x3E, + F_EQUALE = 0x3f, + F_PUSH = 0x40, + S_PUSH = 0x41, + S_POP = 0x42, + S_ADD = 0x43, + S_ASSIGN = 0x44, + S_PLUSA = 0x45, + S_REF = 0x46, + S_REFREF = 0x47, + S_NOTE = 0x48, + S_EQUALE = 0x49, + SF_CREATE = 0x4A, + SF_CREATEPIXEL = 0x4B, + SF_CREATEALPHA = 0x4C, + SR_POP = 0x4d, + SR_ASSIGN = 0x4e, + SR_REF = 0x4f, + SR_REFREF = 0x50, + A_ALLOC = 0x51, + A_REALLOC = 0x52, + A_FREE = 0x53, + A_NUMOF = 0x54, + A_COPY = 0x55, + A_FILL = 0x56, + C_REF = 0x57, + C_ASSIGN = 0x58, + MSG = 0x59, + CALLHLL = 0x5a, + PUSHSTRUCTPAGE = 0x5b, + CALLMETHOD = 0x5c, + SH_GLOBALREF = 0x5d, + SH_LOCALREF = 0x5e, + SWITCH = 0x5f, + STRSWITCH = 0x60, + FUNC = 0x61, + _EOF = 0x62, + CALLSYS = 0x63, + SJUMP = 0x64, + CALLONJUMP = 0x65, + SWAP = 0x66, + SH_STRUCTREF = 0x67, + S_LENGTH = 0x68, + S_LENGTHBYTE = 0x69, + I_STRING = 0x6a, + CALLFUNC2 = 0x6b, + DUP2_X1 = 0x6c, + R_ASSIGN = 0x6d, + FT_ASSIGNS = 0x6e, + ASSERT = 0x6f, + S_LT = 0x70, + S_GT = 0x71, + S_LTE = 0x72, + S_GTE = 0x73, + S_LENGTH2 = 0x74, + S_LENGTHBYTE2 = 0x75, + NEW = 0x76, + DELETE = 0x77, + CHECKUDO = 0x78, + A_REF = 0x79, + DUP = 0x7a, + DUP_U2 = 0x7b, + SP_INC = 0x7c, + SP_DEC = 0x7d, + ENDFUNC = 0x7e, + R_EQUALE = 0x7f, + R_NOTE = 0x80, + SH_LOCALCREATE = 0x81, + SH_LOCALDELETE = 0x82, + STOI = 0x83, + A_PUSHBACK = 0x84, + A_POPBACK = 0x85, + S_EMPTY = 0x86, + A_EMPTY = 0x87, + A_ERASE = 0x88, + A_INSERT = 0x89, + SH_LOCALINC = 0x8a, + SH_LOCALDEC = 0x8b, + SH_LOCALASSIGN = 0x8c, + ITOB = 0x8d, + S_FIND = 0x8e, + S_GETPART = 0x8f, + A_SORT = 0x90, + S_PUSHBACK = 0x91, + S_POPBACK = 0x92, + FTOS = 0x93, + S_MOD = 0x94, + S_PLUSA2 = 0x95, + OBJSWAP = 0x96, + S_ERASE = 0x97, + SR_REF2 = 0x98, + S_ERASE2 = 0x99, + S_PUSHBACK2 = 0x9A, + S_POPBACK2 = 0x9B, + ITOLI = 0x9c, + LI_ADD = 0x9d, + LI_SUB = 0x9e, + LI_MUL = 0x9f, + LI_DIV = 0xa0, + LI_MOD = 0xa1, + LI_ASSIGN = 0xa2, + LI_PLUSA = 0xa3, + LI_MINUSA = 0xa4, + LI_MULA = 0xa5, + LI_DIVA = 0xa6, + LI_MODA = 0xa7, + LI_ANDA = 0xa8, + LI_ORA = 0xa9, + LI_XORA = 0xaa, + LI_LSHIFTA = 0xab, + LI_RSHIFTA = 0xac, + LI_INC = 0xad, + LI_DEC = 0xae, + A_FIND = 0xaf, + A_REVERSE = 0xb0, + SH_SR_ASSIGN, + SH_MEM_ASSIGN_LOCAL, + A_NUMOF_GLOB_1, + A_NUMOF_STRUCT_1, + SH_MEM_ASSIGN_IMM, + SH_LOCALREFREF, + SH_LOCALASSIGN_SUB_IMM, + SH_IF_LOC_LT_IMM, + SH_IF_LOC_GE_IMM, + SH_LOCREF_ASSIGN_MEM, + PAGE_REF, + SH_GLOBAL_ASSIGN_LOCAL, + SH_STRUCTREF_GT_IMM, + SH_STRUCT_ASSIGN_LOCALREF_ITOB, + SH_LOCAL_ASSIGN_STRUCTREF, + SH_IF_STRUCTREF_NE_LOCALREF, + SH_IF_STRUCTREF_GT_IMM, + SH_STRUCTREF_CALLMETHOD_NO_PARAM, + SH_STRUCTREF2, + SH_REF_STRUCTREF2, + SH_STRUCTREF3, + SH_STRUCTREF2_CALLMETHOD_NO_PARAM, + SH_IF_STRUCTREF_Z, + SH_IF_STRUCT_A_NOT_EMPTY, + SH_IF_LOC_GT_IMM, + SH_IF_STRUCTREF_NE_IMM, + THISCALLMETHOD_NOPARAM, + SH_IF_LOC_NE_IMM, + SH_IF_STRUCTREF_EQ_IMM, + SH_GLOBAL_ASSIGN_IMM, + SH_LOCALSTRUCT_ASSIGN_IMM, + SH_STRUCT_A_PUSHBACK_LOCAL_STRUCT, + SH_GLOBAL_A_PUSHBACK_LOCAL_STRUCT, + SH_LOCAL_A_PUSHBACK_LOCAL_STRUCT, + SH_IF_SREF_NE_STR0, + SH_S_ASSIGN_REF, + SH_A_FIND_SREF, + SH_SREF_EMPTY, + SH_STRUCTSREF_EQ_LOCALSREF, + SH_LOCALSREF_EQ_STR0, + SH_STRUCTSREF_NE_LOCALSREF, + SH_LOCALSREF_NE_STR0, + SH_STRUCT_SR_REF, + SH_STRUCT_S_REF, + S_REF2, + SH_REF_LOCAL_ASSIGN_STRUCTREF2, + SH_GLOBAL_S_REF, + SH_LOCAL_S_REF, + SH_LOCALREF_SASSIGN_LOCALSREF, + SH_LOCAL_APUSHBACK_LOCALSREF, + SH_S_ASSIGN_CALLSYS19, + SH_S_ASSIGN_STR0, + SH_SASSIGN_LOCALSREF, + SH_STRUCTREF_SASSIGN_LOCALSREF, + SH_LOCALSREF_EMPTY, + SH_GLOBAL_APUSHBACK_LOCALSREF, + SH_STRUCT_APUSHBACK_LOCALSREF, + SH_STRUCTSREF_EMPTY, + SH_GLOBALSREF_EMPTY, + SH_SASSIGN_STRUCTSREF, + SH_SASSIGN_GLOBALSREF, + SH_STRUCTSREF_NE_STR0, + SH_GLOBALSREF_NE_STR0, + SH_LOC_LT_IMM_OR_LOC_GE_IMM, + // instruction names are known, exact order of them are guesses after this point + A_SORT_MEM, + DG_ADD, + DG_SET, + DG_CALL, + DG_NUMOF, + DG_EXIST, + DG_ERASE, + DG_CLEAR, + DG_COPY, + DG_ASSIGN, + DG_PLUSA, + DG_POP, + DG_NEW_FROM_METHOD, + DG_MINUSA, + DG_CALLBEGIN, + DG_NEW, + DG_STR_TO_METHOD, + + NR_OPCODES +}; + +struct instruction { + const enum opcode opcode; // the opcode + const char * const name; // assembler name + const int nr_args; // number of arguments (???: always 1 or 0?) + const int ip_inc; // amount to increment instruction pointer after instruction +}; + +const struct instruction instructions[NR_OPCODES]; + +static const_pure inline int32_t instruction_width(enum opcode opcode) +{ + return 2 + instructions[opcode].nr_args * 4; +} + +#endif /* SYSTEM4_INSTRUCTIONS_H */ diff --git a/little_endian.h b/little_endian.h new file mode 100644 index 0000000..b48031f --- /dev/null +++ b/little_endian.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * Based on code from xsystem35 + * Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * + * 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 . + */ + +#ifndef LITTLE_ENDIAN_H +#define LITTLE_ENDIAN_H + +#include + +static inline int32_t LittleEndian_getDW(const uint8_t *b, int i) +{ + int c0, c1, c2, c3; + int d0, d1; + c0 = *(b + i + 0); + c1 = *(b + i + 1); + c2 = *(b + i + 2); + c3 = *(b + i + 3); + d0 = c0 + (c1 << 8); + d1 = c2 + (c3 << 8); + return d0 + (d1 << 16); +} + +static inline int32_t LittleEndian_get3B(const uint8_t *b, int i) +{ + int c0, c1, c2; + c0 = *(b + i + 0); + c1 = *(b + i + 1); + c2 = *(b + i + 2); + return c0 + (c1 << 8) + (c2 << 16); +} + +static inline int16_t LittleEndian_getW(const uint8_t *b, int i) +{ + int c0, c1; + c0 = *(b + i + 0); + c1 = *(b + i + 1); + return c0 + (c1 << 8); +} + +static inline void LittleEndian_putDW(uint8_t *dst, int i, uint32_t dword) +{ + dst[i] = (uint8_t)(dword); + dst[i+1] = (uint8_t)(dword >> 8); + dst[i+2] = (uint8_t)(dword >> 16); + dst[i+3] = (uint8_t)(dword >> 24); +} + +#endif /* LITTLE_ENDIAN_H */ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..5ad3951 --- /dev/null +++ b/meson.build @@ -0,0 +1,4 @@ +project('system4', 'c') +system4 = ['system4.c', 'ain.c', 'instructions.c', 'vm.c', 'string.c'] +zdep = dependency('zlib') +executable('system4', system4, dependencies: zdep) diff --git a/string.c b/string.c new file mode 100644 index 0000000..54c2ceb --- /dev/null +++ b/string.c @@ -0,0 +1,62 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#include +#include +#include +#include "system4.h" +#include "sys4_string.h" + +struct string make_string(char *str, size_t len) +{ + return (struct string) { + .str = str, + .len = len, + .mutable = false + }; +} + +void free_string(struct string *str) +{ + if (str->mutable) + free(str->str); +} + +void string_add(struct string *a, struct string *b) +{ + if (a->mutable) { + a->str = xrealloc(a->str, a->len + b->len + 1); + } else { + char *literal = a->str; + a->str = xmalloc(a->len + b->len + 1); + memcpy(a->str, literal, a->len + 1); + } + memcpy(a->str + a->len, b->str, b->len + 1); + a->len = a->len + b->len; +} + +struct string integer_to_string(int n) +{ + char buf[513]; + int len = snprintf(buf, 512, "%d" ,n); + buf[512] = '\0'; + + return (struct string) { + .str = xstrdup(buf), + .len = len, + .mutable = true + }; +} diff --git a/sys4_string.h b/sys4_string.h new file mode 100644 index 0000000..82f506f --- /dev/null +++ b/sys4_string.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#ifndef SYSTEM4_STRING_H +#define SYSTEM4_STRING_H + +#include +#include + +struct string { + char *str; + size_t len; + bool mutable; +}; + +struct string make_string(char *str, size_t len); +void free_string(struct string *str); +void string_add(struct string *a, struct string *b); +struct string integer_to_string(int n); + +#endif diff --git a/system4.c b/system4.c new file mode 100644 index 0000000..bba20c7 --- /dev/null +++ b/system4.c @@ -0,0 +1,101 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#include +#include +#include +#include + +#include "system4.h" +#include "ain.h" + +mem_alloc void *_xmalloc(size_t size, const char *func) +{ + void *ptr = malloc(size); + if (!ptr) { + sys_error("*ERROR*(%s): Out of memory\n", func); + } + return ptr; +} + +mem_alloc void *_xrealloc(void *ptr, size_t size, const char *func) +{ + ptr = realloc(ptr, size); + if (!ptr) { + sys_error("*ERROR*(%s): Out of memory\n", func); + } + return ptr; +} + +mem_alloc char *_xstrdup(const char *in, const char *func) +{ + char *out = strdup(in); + if (!out) { + sys_error("*ERROR*(%s): Out of memory\n", func); + } + return out; +} + +noreturn void sys_error(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + sys_exit(1); +} + +void sys_warning(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +void sys_message(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + fflush(stdout); +} + +noreturn void sys_exit(int code) +{ + // TODO: cleanup + exit(code); +} + +int main(int argc, char *argv[]) +{ + int err = AIN_SUCCESS; + struct ain *ain; + + if (argc != 2) { + printf("wrong number of arguments\n"); + return 1; + } + + if (!(ain = ain_open(argv[1], &err))) { + ERROR("%s", ain_strerror(err)); + } + + vm_execute(ain); + sys_exit(0); +} diff --git a/system4.h b/system4.h new file mode 100644 index 0000000..6446adc --- /dev/null +++ b/system4.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#ifndef SYSTEM4_H +#define SYSTEM4_H + +#include + +// TODO: should put in a separate header and guard with feature checks +#define const_pure __attribute__((const)) +#define mem_alloc __attribute__((malloc)) +#define noreturn _Noreturn + +#define ERROR(fmt, ...) \ + sys_error("*ERROR*(%s): " fmt "\n", __func__, ##__VA_ARGS__) + +#define WARNING(fmt, ...) \ + sys_error("*ERROR*(%s): " fmt "\n", __func__, ##__VA_ARGS__) + +#define NOTICE(fmt, ...) \ + sys_message(fmt "\n", ##__VA_ARGS__) + +noreturn void sys_error(char *fmt, ...); +void sys_warning(char *fmt, ...); +void sys_message(char *fmt, ...); + +noreturn void sys_exit(int code); + +#define xmalloc(size) _xmalloc(size, __func__) +mem_alloc void *_xmalloc(size_t size, const char *func); + +#define xrealloc(ptr, size) _xrealloc(ptr, size, __func__) +mem_alloc void *_xrealloc(void *ptr, size_t size, const char *func); + +#define xstrdup(str) _xstrdup(str, __func__) +mem_alloc char *_xstrdup(const char *in, const char *func); + +#define max(a, b) \ + ({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + +#define min(a, b) \ + ({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; \ + }) + +#endif /* SYSTEM4_H */ diff --git a/vm.c b/vm.c new file mode 100644 index 0000000..e548323 --- /dev/null +++ b/vm.c @@ -0,0 +1,329 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#include +#include + +#include "system4.h" +#include "sys4_string.h" +#include "ain.h" +#include "instructions.h" +#include "little_endian.h" + +/* + * NOTE: The current implementation is a simple bytecode interpreter. + * System40.exe uses a JIT compiler, and we should too. + */ + +static int32_t *stack = NULL; +static size_t stack_size; +static int stack_ptr = 0; + +// Separate stack for strings +// Not sure exactly how System40.exe handles strings. +// Further testing required to ensure this approach doesn't break anything. +static struct string string_stack[1024]; +static int string_stack_ptr = 0; + +//static struct frame *frame_stack[1024]; +static int32_t frame_stack[4096]; +static int frame_ptr = 0; + +#define FRAME_FN_OFF 0 // offset to function number +#define FRAME_IP_OFF 1 // offset to return address +#define FRAME_FP_OFF 2 // offset to frame pointer +#define FRAME_VAR_OFF 3 // offset to variables + +static struct ain *ain; +static size_t instr_ptr = 0; + +static int32_t local_ref(int varno) +{ + return frame_stack[frame_ptr + FRAME_VAR_OFF + varno]; +} + +static void local_set(int varno, int32_t value) +{ + frame_stack[frame_ptr + FRAME_VAR_OFF + varno] = value; +} + +// Read the opcode at ADDR. +static int16_t get_opcode(size_t addr) +{ + return LittleEndian_getW(ain->code, addr); +} + +// Read argument N for the current instruction. +static int32_t get_argument(int n) +{ + return LittleEndian_getDW(ain->code, instr_ptr + 2 + n*4); +} + +static void stack_push(int32_t v) +{ + stack[stack_ptr++] = v; +} + +static int32_t stack_pop(void) +{ + stack_ptr--; + return stack[stack_ptr]; +} + +// Pop a reference off the stack, returning the address of the referenced object. +static int32_t stack_pop_ref(void) +{ + int32_t index = stack_pop(); + int32_t frame = stack_pop(); + return frame + FRAME_VAR_OFF + index; +} + +static void stack_push_string_literal(int32_t no) +{ + // FIXME: should store string length when reading AIN + string_stack[string_stack_ptr++] = make_string(ain->strings[no], strlen(ain->strings[no])); +} + +static void stack_push_string(struct string *str) +{ + string_stack[string_stack_ptr++] = *str; +} + +static struct string stack_pop_string(void) +{ + string_stack_ptr--; + return string_stack[string_stack_ptr]; +} + +static void stack_toss_string(void) +{ + string_stack_ptr--; + free_string(&string_stack[string_stack_ptr]); +} + +static struct string *stack_peek_string(void) +{ + return &string_stack[string_stack_ptr-1]; +} + +/* + * System 4 calling convention: + * - caller pushes arguments, in order + * - CALLFUNC creates stack frame ("page", on separate stack), pops arguments + * - callee pushes return value on the stack + * - RETURN jumps to return address (saved in stack frame) + */ + +static void function_call(int32_t no) +{ + int32_t cur_fno = frame_stack[frame_ptr + FRAME_FN_OFF]; + int32_t new_fp = frame_ptr + FRAME_VAR_OFF + ain->functions[cur_fno].nr_vars; + + // create new stack frame + frame_stack[new_fp + FRAME_FN_OFF] = no; + frame_stack[new_fp + FRAME_IP_OFF] = instr_ptr + instruction_width(CALLFUNC); + frame_stack[new_fp + FRAME_FP_OFF] = frame_ptr; + for (int i = ain->functions[no].nr_args - 1; i >= 0; i--) { + frame_stack[new_fp + FRAME_VAR_OFF + i] = stack_pop(); + } + + // update frame & instruction pointers + frame_ptr = new_fp; + instr_ptr = ain->functions[no].address; +} + +static void function_return(void) +{ + instr_ptr = frame_stack[frame_ptr + FRAME_IP_OFF]; + frame_ptr = frame_stack[frame_ptr + FRAME_FP_OFF]; +} + +static void system_call(int32_t code) +{ + switch (code) { + case 0x6: // system.Output(string szText) + sys_message("%s", stack_peek_string()->str); + // XXX: caller S_POPs + break; + case 0x14: // system.Peek() + break; + case 0x15: // system.Sleep(int nSleep) + stack_pop(); + break; + default: + WARNING("Unimplemented syscall: 0x%X", code); + } +} + +static void execute_instruction(int16_t opcode) +{ + int32_t index, a, b; + struct string sa, sb; + const char *opcode_name = "UNKNOWN"; + switch (opcode) { + // + // --- Stack Management --- + // + case PUSH: + stack_push(get_argument(0)); + break; + case POP: + stack_pop(); + break; + case REF: + // Dereference a reference to a value. + stack_push(frame_stack[stack_pop_ref()]); + break; + case REFREF: + // Dereference a reference to a reference. + index = stack_pop_ref(); + stack_push(frame_stack[index]); + stack_push(frame_stack[index + 1]); + break; + case PUSHLOCALPAGE: + stack_push(frame_ptr); + break; + case SH_LOCALREF: // VARNO + // Push the value of VARNO to the stack + stack_push(local_ref(get_argument(0))); + break; + case SH_LOCALASSIGN: // VARNO, VALUE + // Assign VALUE to local VARNO + local_set(get_argument(0), get_argument(1)); + break; + case SH_LOCALINC: // VARNO + index = get_argument(0); + local_set(index, local_ref(index)+1); + break; + case SH_LOCALDEC: // VARNO + index = get_argument(0); + local_set(index, local_ref(index)-1); + break; + // + // --- Function Calls --- + // + case CALLFUNC: + function_call(get_argument(0)); + break; + case RETURN: + function_return(); + break; + case CALLSYS: + system_call(get_argument(0)); + break; + // + // --- Control Flow --- + // + case JUMP: // ADDR + instr_ptr = get_argument(0); + break; + case IFZ: // ADDR + if (!stack_pop()) + instr_ptr = get_argument(0); + else + instr_ptr += instruction_width(IFZ); + break; + case IFNZ: // ADDR + if (stack_pop()) + instr_ptr = get_argument(0); + else + instr_ptr += instruction_width(IFNZ); + break; + // + // --- Arithmetic --- + // + case LT: + b = stack_pop(); + a = stack_pop(); + stack_push(a < b ? 1 : 0); + break; + case GT: + b = stack_pop(); + a = stack_pop(); + stack_push(a > b ? 1 : 0); + break; + case LTE: + b = stack_pop(); + a = stack_pop(); + stack_push(a <= b ? 1 : 0); + break; + case GTE: + b = stack_pop(); + a = stack_pop(); + stack_push(a >= b ? 1 : 0); + break; + case NOTE: + b = stack_pop(); + a = stack_pop(); + stack_push(a != b ? 1 : 0); + break; + case EQUALE: + b = stack_pop(); + a = stack_pop(); + stack_push(a == b ? 1 : 0); + break; + // + // --- Strings --- + // + case S_PUSH: + stack_push_string_literal(get_argument(0)); + break; + case S_POP: + stack_toss_string(); + break; + case S_ADD: + sb = stack_pop_string(); + sa = stack_pop_string(); + string_add(&sa, &sb); + stack_push_string(&sa); + break; + case I_STRING: + sa = integer_to_string(stack_pop()); + stack_push_string(&sa); + break; + // -- NOOPs --- + case FUNC: + break; + default: + if (opcode >= 0 && opcode < NR_OPCODES && instructions[opcode].name) { + opcode_name = instructions[opcode].name; + } + WARNING("Unimplemented instruction: 0x%X(%s)", opcode, opcode_name); + } +} + +void vm_execute(struct ain *program) +{ + // initialize machine state + stack_ptr = 0; + frame_ptr = 0; + stack_size = 1024; + stack = xmalloc(stack_size); + ain = program; + + // jump to main and begin fetch-decode-execute cycle + //instr_ptr = ain->functions[ain->main].address; + function_call(ain->main); + for ( ; instr_ptr < ain->code_size; ) + { + if (instr_ptr >= ain->code_size) { + ERROR("Illegal instruction pointer: 0x%lX", instr_ptr); + } + int16_t opcode = get_opcode(instr_ptr); + execute_instruction(opcode); + instr_ptr += instructions[opcode].ip_inc; + } +}