mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-30 19:08:12 +03:00
Add aincmp tool
Tool for comparing ain files. This is used by the script rtt.sh to ensure that disassembling and then reassembling an ain file produces an equivalent result.
This commit is contained in:
@@ -0,0 +1,623 @@
|
||||
/* 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 <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <getopt.h>
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/instructions.h"
|
||||
#include "system4/string.h"
|
||||
#include "../dasm.h"
|
||||
|
||||
static void ain_compare(struct ain *a, struct ain *b);
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
puts("Usage: aincmp <ainfile> <ainfile>");
|
||||
puts(" Compare two ain files");
|
||||
}
|
||||
|
||||
enum {
|
||||
LOPT_HELP = 256,
|
||||
};
|
||||
|
||||
static int exit_code = 0;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
initialize_instructions();
|
||||
|
||||
int err;
|
||||
struct ain *a, *b;
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{ "help", no_argument, 0, LOPT_HELP },
|
||||
};
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "h", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
case LOPT_HELP:
|
||||
usage();
|
||||
return 0;
|
||||
case '?':
|
||||
ERROR("Unexpected command line argument");
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc != 2) {
|
||||
usage();
|
||||
ERROR("Wrong number of arguments");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(a = ain_open(argv[0], &err)) || !(b = ain_open(argv[1], &err))) {
|
||||
ERROR("Failed to open ain file: %s", ain_strerror(err));
|
||||
}
|
||||
|
||||
ain_compare(a, b);
|
||||
ain_free(a);
|
||||
ain_free(b);
|
||||
puts(exit_code ? "AIN files differ" : "AIN files match");
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
static void _ain_compare_section(struct ain_section *a, struct ain_section *b, const char *name)
|
||||
{
|
||||
if (a->present != b->present || a->size != b->size) {
|
||||
printf("%s section differs", name);
|
||||
exit_code = 1;
|
||||
}
|
||||
}
|
||||
#define ain_compare_section(a, b, name) _ain_compare_section(&a->name , &b->name , #name)
|
||||
|
||||
#define FLOAT_TOLERANCE 0.0001
|
||||
|
||||
union float_cast {
|
||||
int32_t i;
|
||||
float f;
|
||||
};
|
||||
|
||||
static float float_cast(int32_t i)
|
||||
{
|
||||
union float_cast u = { .i = i };
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static bool float_equal(int32_t a, int32_t b)
|
||||
{
|
||||
return fabsf(float_cast(a) - float_cast(b)) < FLOAT_TOLERANCE;
|
||||
}
|
||||
|
||||
static bool ain_compare_code(struct ain *_a, struct ain *_b)
|
||||
{
|
||||
struct dasm_state a, b;
|
||||
dasm_init(&a, NULL, _a, 0);
|
||||
dasm_init(&b, NULL, _b, 0);
|
||||
|
||||
for (dasm_reset(&a), dasm_reset(&b); !dasm_eof(&a) && !dasm_eof(&b); dasm_next(&a), dasm_next(&b)) {
|
||||
if (a.instr->opcode != b.instr->opcode) {
|
||||
printf("opcode differs at 0x%08x (%s vs %s)", (uint32_t)a.addr, a.instr->name, b.instr->name);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < a.instr->nr_args; i++) {
|
||||
int32_t ia = dasm_arg(&a, i);
|
||||
int32_t ib = dasm_arg(&b, i);
|
||||
if (a.instr->args[i] == T_FLOAT) {
|
||||
if (!float_equal(ia, ib)) {
|
||||
printf("float argument differs at 0x%08x (%f vs %f)", (uint32_t)a.addr,
|
||||
float_cast(ia), float_cast(ib));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (ia != ib) {
|
||||
printf("argument differs at 0x%08x (%d vs %d)", (uint32_t)a.addr, ia, ib);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool type_equal(struct ain_type *a, struct ain_type *b)
|
||||
{
|
||||
if (a->data != b->data || a->struc != b->struc || a->rank != b->rank)
|
||||
return false;
|
||||
if ((a->array_type && !b->array_type) || (b->array_type && !a->array_type))
|
||||
return false;
|
||||
if (!a->array_type)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < a->rank; i++) {
|
||||
if (!type_equal(&a->array_type[i], &b->array_type[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool variable_equal(struct ain_variable *a, struct ain_variable *b)
|
||||
{
|
||||
if (strcmp(a->name, b->name))
|
||||
return false;
|
||||
if (!a->name2 != !b->name2)
|
||||
return false;
|
||||
if (a->name2 && strcmp(a->name2, b->name2))
|
||||
return false;
|
||||
if (!type_equal(&a->type, &b->type))
|
||||
return false;
|
||||
if (a->has_initval != b->has_initval)
|
||||
return false;
|
||||
// TODO: compare initvals
|
||||
if (a->group_index != b->group_index)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_functions(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_functions != b->nr_functions) {
|
||||
printf("number of functions differs (%d vs %d)", a->nr_functions, b->nr_functions);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_functions; i++) {
|
||||
struct ain_function *fa = &a->functions[i], *fb = &b->functions[i];
|
||||
if (strcmp(fa->name, fb->name)) {
|
||||
printf("function name differs (\"%s\" vs \"%s\")", fa->name, fb->name);
|
||||
return false;
|
||||
}
|
||||
if (fa->address != fb->address) {
|
||||
printf("function address differs for %s (0x%08x vs 0x%08x)", fa->name, fa->address, fb->address);
|
||||
return false;
|
||||
}
|
||||
if (fa->is_label != fb->is_label) {
|
||||
printf("function is_label differs for %s (%d vs %d)", fa->name, fa->is_label, fb->is_label);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!type_equal(&fa->return_type, &fb->return_type)) {
|
||||
printf("function return type differs for %s", fa->name); // TODO: print types
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fa->nr_args != fb->nr_args) {
|
||||
printf("function argument count differs for %s (%d vs %d)", fa->name, fa->nr_args, fb->nr_args);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fa->nr_vars != fb->nr_vars) {
|
||||
printf("function variable count differs for %s (%d vs %d)", fa->name, fa->nr_vars, fb->nr_vars);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fa->is_lambda != fb->is_lambda) {
|
||||
printf("function is_lambda differs for %s (%d vs %d)", fa->name, fa->is_lambda, fb->is_lambda);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fa->crc != fb->crc) {
|
||||
printf("function crc differs for %s (%d vs %d)", fa->name, fa->crc, fb->crc);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int j = 0; j < fa->nr_vars; j++) {
|
||||
if (!variable_equal(&fa->vars[j], &fb->vars[j])) {
|
||||
printf("function variable %d differs for %s", j, fa->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_globals(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_globals != b->nr_globals) {
|
||||
printf("number of globals differs (%d vs %d)", a->nr_globals, b->nr_globals);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_globals; i++) {
|
||||
if (!variable_equal(&a->globals[i], &b->globals[i])) {
|
||||
printf("global variable %d (%s) differs", i, a->globals[i].name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool initval_equal(struct ain_initval *a, struct ain_initval *b)
|
||||
{
|
||||
if (a->global_index != b->global_index)
|
||||
return false;
|
||||
if (a->data_type != b->data_type)
|
||||
return false;
|
||||
if (a->data_type == AIN_STRING)
|
||||
return !strcmp(a->string_value, b->string_value);
|
||||
if (a->data_type == AIN_FLOAT)
|
||||
return float_equal(a->int_value, b->int_value);
|
||||
return a->int_value == b->int_value;
|
||||
}
|
||||
|
||||
static bool ain_compare_global_initvals(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_initvals != b->nr_initvals) {
|
||||
printf("number of global initvals differs (%d vs %d)", a->nr_initvals, b->nr_initvals);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_initvals; i++) {
|
||||
if (!initval_equal(&a->global_initvals[i], &b->global_initvals[i])) {
|
||||
printf("global initval %d differs", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool struct_equal(struct ain_struct *a, struct ain_struct *b)
|
||||
{
|
||||
if (strcmp(a->name, b->name))
|
||||
return false;
|
||||
if (a->nr_interfaces != b->nr_interfaces)
|
||||
return false;
|
||||
if (a->constructor != b->constructor)
|
||||
return false;
|
||||
if (a->destructor != b->destructor)
|
||||
return false;
|
||||
if (a->nr_members != b->nr_members)
|
||||
return false;
|
||||
for (int i = 0; i < a->nr_members; i++) {
|
||||
if (!variable_equal(&a->members[i], &b->members[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_structs(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_structures != b->nr_structures) {
|
||||
printf("number of structures differs (%d vs %d)", a->nr_structures, b->nr_structures);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_structures; i++) {
|
||||
if (!struct_equal(&a->structures[i], &b->structures[i])) {
|
||||
printf("structure %s differs", a->structures[i].name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_messages(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_messages != b->nr_messages) {
|
||||
printf("number of messages differs (%d vs %d)", a->nr_messages, b->nr_messages);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_messages; i++) {
|
||||
if (strcmp(a->messages[i]->text, b->messages[i]->text)) {
|
||||
printf("message %d differs", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hll_function_equal(struct ain_hll_function *a, struct ain_hll_function *b)
|
||||
{
|
||||
if (strcmp(a->name, b->name))
|
||||
return false;
|
||||
if (!type_equal(&a->return_type, &b->return_type))
|
||||
return false;
|
||||
if (a->nr_arguments != b->nr_arguments)
|
||||
return false;
|
||||
for (int i = 0; i < a->nr_arguments; i++) {
|
||||
if (strcmp(a->arguments[i].name, b->arguments[i].name))
|
||||
return false;
|
||||
if (!type_equal(&a->arguments[i].type, &b->arguments[i].type))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_library(struct ain_library *a, struct ain_library *b)
|
||||
{
|
||||
if (strcmp(a->name, b->name)) {
|
||||
printf("library name differs (\"%s\" vs \"%s\")", a->name, b->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a->nr_functions != b->nr_functions) {
|
||||
printf("library function count differs for %s (%d vs %d)", a->name, a->nr_functions, b->nr_functions);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_functions; i++) {
|
||||
if (!hll_function_equal(&a->functions[i], &b->functions[i])) {
|
||||
printf("library function %s.%s differs", a->name, a->functions[i].name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_libraries(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_libraries != b->nr_libraries) {
|
||||
printf("number of libraries differs (%d vs %d)", a->nr_libraries, b->nr_libraries);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_libraries; i++) {
|
||||
if (!ain_compare_library(&a->libraries[i], &b->libraries[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool switch_equal(struct ain_switch *a, struct ain_switch *b)
|
||||
{
|
||||
if (a->case_type != b->case_type)
|
||||
return false;
|
||||
if (a->default_address != b->default_address)
|
||||
return false;
|
||||
if (a->nr_cases != b->nr_cases)
|
||||
return false;
|
||||
for (int i = 0; i < a->nr_cases; i++) {
|
||||
if (a->cases[i].value != b->cases[i].value)
|
||||
return false;
|
||||
if (a->cases[i].address != b->cases[i].address)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_switches(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_switches != b->nr_switches) {
|
||||
printf("number of switches differs (%d vs %d)", a->nr_switches, b->nr_switches);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_switches; i++) {
|
||||
if (!switch_equal(&a->switches[i], &b->switches[i])) {
|
||||
printf("switch %d differs", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_strings(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_strings != b->nr_strings) {
|
||||
printf("number of strings differs (%d vs %d)", a->nr_strings, b->nr_strings);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_strings; i++) {
|
||||
if (strcmp(a->strings[i]->text, b->strings[i]->text)) {
|
||||
printf("string %d differs", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_filenames(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_filenames != b->nr_filenames) {
|
||||
printf("number of filenames differs (%d vs %d)", a->nr_filenames, b->nr_filenames);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_filenames; i++) {
|
||||
if (strcmp(a->filenames[i], b->filenames[i])) {
|
||||
printf("filename %d differs", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool function_type_equal(struct ain_function_type *a, struct ain_function_type *b)
|
||||
{
|
||||
if (strcmp(a->name, b->name))
|
||||
return false;
|
||||
if (!type_equal(&a->return_type, &b->return_type))
|
||||
return false;
|
||||
if (a->nr_arguments != b->nr_arguments)
|
||||
return false;
|
||||
if (a->nr_variables != b->nr_variables)
|
||||
return false;
|
||||
for (int i = 0; i < a->nr_variables; i++) {
|
||||
if (!variable_equal(&a->variables[i], &b->variables[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_function_types(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_function_types != b->nr_function_types) {
|
||||
printf("number of function types differs (%d vs %d)", a->nr_function_types, b->nr_function_types);
|
||||
return false;
|
||||
}
|
||||
if (a->nr_delegates != b->nr_delegates) {
|
||||
printf("number of delegates differs (%d vs %d)", a->nr_delegates, b->nr_delegates);
|
||||
return false;
|
||||
}
|
||||
|
||||
int count;
|
||||
struct ain_function_type *types_a, *types_b;
|
||||
if (a->delegates) {
|
||||
types_a = a->delegates;
|
||||
types_b = b->delegates;
|
||||
count = a->nr_delegates;
|
||||
} else {
|
||||
types_a = a->function_types;
|
||||
types_b = b->function_types;
|
||||
count = a->nr_function_types;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!function_type_equal(&types_a[i], &types_b[i])) {
|
||||
printf("functype/delegate %d differs", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_global_groups(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_global_groups != b->nr_global_groups) {
|
||||
printf("number of global groups differs (%d vs %d)", a->nr_global_groups, b->nr_global_groups);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_global_groups; i++) {
|
||||
if (strcmp(a->global_group_names[i], b->global_group_names[i])) {
|
||||
printf("global group %d differs (\"%s\" vs \"%s\")", i, a->global_group_names[i], b->global_group_names[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ain_compare_enums(struct ain *a, struct ain *b)
|
||||
{
|
||||
if (a->nr_enums != b->nr_enums) {
|
||||
printf("number of enums differs (%d vs %d)", a->nr_enums, b->nr_enums);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a->nr_enums; i++) {
|
||||
if (strcmp(a->enums[i].name, b->enums[i].name)) {
|
||||
printf("enum %d differs (\"%s\" vs \"%s\")", i, a->enums[i].name, b->enums[i].name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ain_compare(struct ain *a, struct ain *b)
|
||||
{
|
||||
ain_compare_section(a, b, VERS);
|
||||
if (a->version != b->version) {
|
||||
printf("ain version differs (%d vs %d)", a->version, b->version);
|
||||
exit_code = 1;
|
||||
}
|
||||
|
||||
ain_compare_section(a, b, KEYC);
|
||||
if (a->keycode != b->keycode) {
|
||||
printf("keycode differs (%d vs %d)", a->keycode, b->keycode);
|
||||
exit_code = 1;
|
||||
}
|
||||
|
||||
ain_compare_section(a, b, CODE);
|
||||
if (!ain_compare_code(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, FUNC);
|
||||
if (!ain_compare_functions(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, GLOB);
|
||||
if (!ain_compare_globals(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, GSET);
|
||||
if (!ain_compare_global_initvals(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, STRT);
|
||||
if (!ain_compare_structs(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, MSG0);
|
||||
ain_compare_section(a, b, MSG1);
|
||||
if (!ain_compare_messages(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, MAIN);
|
||||
if (a->main != b->main) {
|
||||
printf("main function differs (%d vs %d)", a->main, b->main);
|
||||
exit_code = 1;
|
||||
}
|
||||
|
||||
ain_compare_section(a, b, MSGF);
|
||||
if (a->msgf != b->msgf) {
|
||||
printf("message function differs (%d vs %d)", a->msgf, b->msgf);
|
||||
exit_code = 1;
|
||||
}
|
||||
|
||||
ain_compare_section(a, b, HLL0);
|
||||
if (!ain_compare_libraries(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, SWI0);
|
||||
if (!ain_compare_switches(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, GVER);
|
||||
if (a->game_version != b->game_version) {
|
||||
printf("game version differs (%d vs %d)", a->game_version, b->game_version);
|
||||
exit_code = 1;
|
||||
}
|
||||
|
||||
ain_compare_section(a, b, STR0);
|
||||
if (!ain_compare_strings(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, FNAM);
|
||||
if (!ain_compare_filenames(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, OJMP);
|
||||
if (a->ojmp != b->ojmp) {
|
||||
printf("ojmp differs (%d vs %d)", a->ojmp, b->ojmp);
|
||||
exit_code = 1;
|
||||
}
|
||||
|
||||
ain_compare_section(a, b, FNCT);
|
||||
ain_compare_section(a, b, DELG);
|
||||
if (!ain_compare_function_types(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, OBJG);
|
||||
if (!ain_compare_global_groups(a, b))
|
||||
exit_code = 1;
|
||||
|
||||
ain_compare_section(a, b, ENUM);
|
||||
if (!ain_compare_enums(a, b))
|
||||
exit_code = 1;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
aincmp = ['../dasm.c',
|
||||
'aincmp.c',
|
||||
]
|
||||
|
||||
executable('aincmp', aincmp,
|
||||
dependencies : tool_deps,
|
||||
include_directories : incdir,
|
||||
link_with : libsys4)
|
||||
@@ -25,8 +25,6 @@
|
||||
#include "system4/instructions.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#define DASM_ERROR(dasm, fmt, ...) ERROR("At 0x%x: " fmt, dasm->addr, ##__VA_ARGS__)
|
||||
|
||||
enum jump_target_type {
|
||||
JMP_LABEL,
|
||||
JMP_CASE,
|
||||
@@ -548,63 +546,6 @@ static void generate_labels(struct dasm_state *dasm)
|
||||
}
|
||||
}
|
||||
|
||||
void dasm_init(struct dasm_state *dasm, FILE *out, struct ain *ain, uint32_t flags)
|
||||
{
|
||||
dasm->out = out;
|
||||
dasm->ain = ain;
|
||||
dasm->flags = flags;
|
||||
dasm->addr = 0;
|
||||
dasm->func = -1;
|
||||
|
||||
for (int i = 0; i < DASM_FUNC_STACK_SIZE; i++) {
|
||||
dasm->func_stack[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void dasm_next(struct dasm_state *dasm)
|
||||
{
|
||||
dasm->addr += instruction_width(dasm->instr->opcode);
|
||||
dasm->instr = dasm_eof(dasm) ? &instructions[0] : dasm_get_instruction(dasm);
|
||||
}
|
||||
|
||||
enum opcode dasm_peek(struct dasm_state *dasm)
|
||||
{
|
||||
int width = instruction_width(dasm->instr->opcode);
|
||||
if (dasm->addr+width >= dasm->ain->code_size)
|
||||
return -1;
|
||||
|
||||
return LittleEndian_getW(dasm->ain->code, dasm->addr+width);
|
||||
}
|
||||
|
||||
bool dasm_eof(struct dasm_state *dasm)
|
||||
{
|
||||
return dasm->addr >= dasm->ain->code_size;
|
||||
}
|
||||
|
||||
void dasm_reset(struct dasm_state *dasm)
|
||||
{
|
||||
dasm->addr = 0;
|
||||
dasm->instr = dasm_eof(dasm) ? &instructions[0] : dasm_get_instruction(dasm);
|
||||
}
|
||||
|
||||
dasm_save_t dasm_save(struct dasm_state *dasm)
|
||||
{
|
||||
return (dasm_save_t) { .addr = dasm->addr, .instr = dasm->instr };
|
||||
}
|
||||
|
||||
void dasm_restore(struct dasm_state *dasm, dasm_save_t save)
|
||||
{
|
||||
dasm->addr = save.addr;
|
||||
dasm->instr = save.instr;
|
||||
}
|
||||
|
||||
int32_t dasm_arg(struct dasm_state *dasm, unsigned int n)
|
||||
{
|
||||
if ((int)n >= dasm->instr->nr_args)
|
||||
return 0;
|
||||
return LittleEndian_getDW(dasm->ain->code, dasm->addr + 2 + 4*n);
|
||||
}
|
||||
|
||||
void disassemble_ain(FILE *out, struct ain *ain, unsigned int flags)
|
||||
{
|
||||
struct dasm_state dasm;
|
||||
|
||||
@@ -20,37 +20,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "../dasm.h"
|
||||
|
||||
#define DASM_FUNC_STACK_SIZE 16
|
||||
|
||||
struct dasm_state {
|
||||
struct ain *ain;
|
||||
uint32_t flags;
|
||||
FILE *out;
|
||||
size_t addr;
|
||||
int func;
|
||||
int func_stack[DASM_FUNC_STACK_SIZE];
|
||||
const struct instruction *instr;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
size_t addr;
|
||||
const struct instruction *instr;
|
||||
} dasm_save_t;
|
||||
|
||||
struct instruction;
|
||||
struct ain_function;
|
||||
|
||||
void dasm_next(struct dasm_state *dasm);
|
||||
enum opcode dasm_peek(struct dasm_state *dasm);
|
||||
bool dasm_eof(struct dasm_state *dasm);
|
||||
void dasm_reset(struct dasm_state *dasm);
|
||||
dasm_save_t dasm_save(struct dasm_state *dasm);
|
||||
void dasm_restore(struct dasm_state *dasm, dasm_save_t save);
|
||||
|
||||
int32_t dasm_arg(struct dasm_state *dasm, unsigned int n);
|
||||
bool dasm_is_jump_target(struct dasm_state *dasm);
|
||||
|
||||
bool dasm_print_macro(struct dasm_state *dasm);
|
||||
void dasm_print_identifier(struct dasm_state *dasm, const char *str);
|
||||
void dasm_print_local_variable(struct dasm_state *dasm, struct ain_function *func, int varno);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
aindump = ['../../cJSON.c',
|
||||
'../conv.c',
|
||||
'../dasm.c',
|
||||
'aindump.c',
|
||||
'dasm.c',
|
||||
'guess_filenames.c',
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/* 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 "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/instructions.h"
|
||||
#include "little_endian.h"
|
||||
#include "dasm.h"
|
||||
|
||||
static const struct instruction *dasm_get_instruction(struct dasm_state *dasm)
|
||||
{
|
||||
uint16_t opcode = LittleEndian_getW(dasm->ain->code, dasm->addr);
|
||||
if (opcode >= NR_OPCODES)
|
||||
DASM_ERROR(dasm, "Unknown/invalid opcode: %u", opcode);
|
||||
|
||||
const struct instruction *instr = &instructions[opcode];
|
||||
if (dasm->addr + instr->nr_args * 4 >= dasm->ain->code_size)
|
||||
DASM_ERROR(dasm, "CODE section truncated?");
|
||||
|
||||
return instr;
|
||||
}
|
||||
|
||||
void dasm_init(struct dasm_state *dasm, FILE *out, struct ain *ain, uint32_t flags)
|
||||
{
|
||||
dasm->out = out;
|
||||
dasm->ain = ain;
|
||||
dasm->flags = flags;
|
||||
dasm->addr = 0;
|
||||
dasm->func = -1;
|
||||
|
||||
for (int i = 0; i < DASM_FUNC_STACK_SIZE; i++) {
|
||||
dasm->func_stack[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void dasm_next(struct dasm_state *dasm)
|
||||
{
|
||||
dasm->addr += instruction_width(dasm->instr->opcode);
|
||||
dasm->instr = dasm_eof(dasm) ? &instructions[0] : dasm_get_instruction(dasm);
|
||||
}
|
||||
|
||||
enum opcode dasm_peek(struct dasm_state *dasm)
|
||||
{
|
||||
int width = instruction_width(dasm->instr->opcode);
|
||||
if (dasm->addr+width >= dasm->ain->code_size)
|
||||
return -1;
|
||||
|
||||
return LittleEndian_getW(dasm->ain->code, dasm->addr+width);
|
||||
}
|
||||
|
||||
bool dasm_eof(struct dasm_state *dasm)
|
||||
{
|
||||
return dasm->addr >= dasm->ain->code_size;
|
||||
}
|
||||
|
||||
void dasm_reset(struct dasm_state *dasm)
|
||||
{
|
||||
dasm->addr = 0;
|
||||
dasm->instr = dasm_eof(dasm) ? &instructions[0] : dasm_get_instruction(dasm);
|
||||
}
|
||||
|
||||
dasm_save_t dasm_save(struct dasm_state *dasm)
|
||||
{
|
||||
return (dasm_save_t) { .addr = dasm->addr, .instr = dasm->instr };
|
||||
}
|
||||
|
||||
void dasm_restore(struct dasm_state *dasm, dasm_save_t save)
|
||||
{
|
||||
dasm->addr = save.addr;
|
||||
dasm->instr = save.instr;
|
||||
}
|
||||
|
||||
int32_t dasm_arg(struct dasm_state *dasm, unsigned int n)
|
||||
{
|
||||
if ((int)n >= dasm->instr->nr_args)
|
||||
return 0;
|
||||
return LittleEndian_getDW(dasm->ain->code, dasm->addr + 2 + 4*n);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef AIN_DASM_H
|
||||
#define AIN_DASM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define DASM_ERROR(dasm, fmt, ...) ERROR("At 0x%x: " fmt, dasm->addr, ##__VA_ARGS__)
|
||||
|
||||
#define DASM_FUNC_STACK_SIZE 16
|
||||
|
||||
struct dasm_state {
|
||||
struct ain *ain;
|
||||
uint32_t flags;
|
||||
FILE *out;
|
||||
size_t addr;
|
||||
int func;
|
||||
int func_stack[DASM_FUNC_STACK_SIZE];
|
||||
const struct instruction *instr;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
size_t addr;
|
||||
const struct instruction *instr;
|
||||
} dasm_save_t;
|
||||
|
||||
void dasm_init(struct dasm_state *dasm, FILE *out, struct ain *ain, uint32_t flags);
|
||||
void dasm_next(struct dasm_state *dasm);
|
||||
enum opcode dasm_peek(struct dasm_state *dasm);
|
||||
bool dasm_eof(struct dasm_state *dasm);
|
||||
void dasm_reset(struct dasm_state *dasm);
|
||||
dasm_save_t dasm_save(struct dasm_state *dasm);
|
||||
void dasm_restore(struct dasm_state *dasm, dasm_save_t save);
|
||||
int32_t dasm_arg(struct dasm_state *dasm, unsigned int n);
|
||||
|
||||
#endif /* AIN_DASM_H */
|
||||
@@ -7,6 +7,7 @@ endif
|
||||
|
||||
subdir('aindump')
|
||||
subdir('ainedit')
|
||||
subdir('aincmp')
|
||||
subdir('ar')
|
||||
subdir('ex')
|
||||
subdir('fnl')
|
||||
|
||||
Reference in New Issue
Block a user