diff --git a/common/common_types.h b/common/common_types.h index f9ce83170c..5c9c55e267 100644 --- a/common/common_types.h +++ b/common/common_types.h @@ -5,9 +5,6 @@ * Common Integer Types. */ -#ifndef JAK1_COMMON_TYPES_H -#define JAK1_COMMON_TYPES_H - #include using u8 = uint8_t; @@ -22,12 +19,14 @@ using s64 = int64_t; struct u128 { union { u64 du64[2]; + s64 ds64[2]; u32 du32[4]; + s32 ds32[4]; u16 du16[8]; + s16 ds16[8]; u8 du8[16]; + s8 ds8[16]; float f[4]; }; }; static_assert(sizeof(u128) == 16, "u128"); - -#endif // JAK1_COMMON_TYPES_H diff --git a/common/link_types.h b/common/link_types.h index 25fa99fd45..4ea702a835 100644 --- a/common/link_types.h +++ b/common/link_types.h @@ -18,6 +18,8 @@ enum LinkKind { enum SegmentTypes { MAIN_SEGMENT = 0, DEBUG_SEGMENT = 1, TOP_LEVEL_SEGMENT = 2 }; +constexpr const char* SEGMENT_NAMES[3] = {"main", "debug", "top-level"}; + constexpr int N_SEG = 3; /*! diff --git a/common/symbols.h b/common/symbols.h index fb2c3ab42f..2e9f5fda82 100644 --- a/common/symbols.h +++ b/common/symbols.h @@ -5,9 +5,6 @@ * The location of fixed symbols in the GOAL symbol table. */ -#ifndef JAK1_SYMBOLS_H -#define JAK1_SYMBOLS_H - constexpr int FIX_SYM_EMPTY_CAR = -0xc; constexpr int FIX_SYM_EMPTY_PAIR = -0xa; constexpr int FIX_SYM_EMPTY_CDR = -0x8; @@ -79,5 +76,3 @@ constexpr int FIX_SYM_SOUND = 0x1b8; // ?? constexpr int FIX_SYM_DGO = 0x1c0; // ?? constexpr int FIX_SYM_TOP_LEVEL = 0x1c8; // ?? constexpr int FIX_FIXED_SYM_END_OFFSET = 0x1d0; - -#endif // JAK1_SYMBOLS_H diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index af05b22dee..e19801f7e0 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -10,6 +10,7 @@ add_library( analysis/inline_asm_rewrite.cpp analysis/insert_lets.cpp analysis/label_types.cpp + analysis/mips2c.cpp analysis/reg_usage.cpp analysis/stack_spill.cpp analysis/static_refs.cpp diff --git a/decompiler/Disasm/InstructionMatching.h b/decompiler/Disasm/InstructionMatching.h index 2f14fabe0a..b9efc44746 100644 --- a/decompiler/Disasm/InstructionMatching.h +++ b/decompiler/Disasm/InstructionMatching.h @@ -5,9 +5,6 @@ * Utilities for checking if an instruction matches some criteria. */ -#ifndef JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H -#define JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H - #include "Instruction.h" #include "decompiler/util/MatchParam.h" @@ -58,4 +55,3 @@ Register make_fpr(int fpr); bool is_branch(const Instruction& instr, MatchParam likely); bool is_always_branch(const Instruction& instr); } // namespace decompiler -#endif // JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H diff --git a/decompiler/Function/Function.cpp b/decompiler/Function/Function.cpp index 5270b1d389..87724139aa 100644 --- a/decompiler/Function/Function.cpp +++ b/decompiler/Function/Function.cpp @@ -78,7 +78,7 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { lg::warn( "Function {} was flagged as asm due to this instruction: {}. Consider flagging as asm " "in config!", - guessed_name.to_string(), instr.to_string(file.labels)); + name(), instr.to_string(file.labels)); warnings.general_warning("Flagged as asm because of {}", instr.to_string(file.labels)); suspected_asm = true; return; @@ -103,7 +103,7 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { lg::warn( "Function {} was flagged as asm due to this instruction: {}. Consider flagging as asm " "in config!", - guessed_name.to_string(), instr.to_string(file.labels)); + name(), instr.to_string(file.labels)); warnings.general_warning("Flagged as asm because of {}", instr.to_string(file.labels)); suspected_asm = true; return; @@ -158,7 +158,7 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { if (this_reg != get_expected_gpr_backup(i, n_gpr_backups)) { suspected_asm = true; lg::warn("Function {} stores on the stack in a strange way ({}), flagging as asm!", - instructions.at(idx + i).to_string(file.labels), guessed_name.to_string()); + instructions.at(idx + i).to_string(file.labels), name()); warnings.general_warning("Flagged as asm due to strange stack store: {}", instructions.at(idx + i).to_string(file.labels)); return; @@ -187,7 +187,7 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { if (this_reg != get_expected_fpr_backup(i, n_fpr_backups)) { suspected_asm = true; lg::warn("Function {} stores on the stack in a strange way ({}), flagging as asm!", - instructions.at(idx + i).to_string(file.labels), guessed_name.to_string()); + instructions.at(idx + i).to_string(file.labels), name()); warnings.general_warning("Flagged as asm due to strange stack store: {}", instructions.at(idx + i).to_string(file.labels)); return; @@ -378,8 +378,7 @@ void Function::check_epilogue(const LinkedObjectFile& file) { idx--; assert(is_jr_ra(instructions.at(idx))); idx--; - lg::warn("Function {} has a double return and is being flagged as asm.", - guessed_name.to_string()); + lg::warn("Function {} has a double return and is being flagged as asm.", name()); warnings.general_warning("Flagged as asm due to double return"); } // delay slot should be daddiu sp, sp, offset @@ -773,4 +772,8 @@ BlockTopologicalSort Function::bb_topo_sort() { return result; } + +std::string Function::name() const { + return guessed_name.to_string(); +} } // namespace decompiler \ No newline at end of file diff --git a/decompiler/Function/Function.h b/decompiler/Function/Function.h index e04227505e..c27aed6a25 100644 --- a/decompiler/Function/Function.h +++ b/decompiler/Function/Function.h @@ -113,6 +113,7 @@ class Function { int get_basic_op_count(); int get_failed_basic_op_count(); BlockTopologicalSort bb_topo_sort(); + std::string name() const; TypeSpec type; @@ -188,6 +189,8 @@ class Function { bool expressions_succeeded = false; } ir2; + std::optional mips2c_output; + std::vector types_defined; private: diff --git a/decompiler/IR2/AtomicOpTypeAnalysis.cpp b/decompiler/IR2/AtomicOpTypeAnalysis.cpp index 23846ae34d..953889c453 100644 --- a/decompiler/IR2/AtomicOpTypeAnalysis.cpp +++ b/decompiler/IR2/AtomicOpTypeAnalysis.cpp @@ -86,7 +86,7 @@ TP_Type SimpleAtom::get_type(const TypeState& input, return TP_Type::make_enter_state(); } else if (m_string == "run-function-in-process") { return TP_Type::make_run_function_in_process_function(); - } else if (m_string == "set-to-run" && env.func->guessed_name.to_string() != "enter-state") { + } else if (m_string == "set-to-run" && env.func->name() != "enter-state") { return TP_Type::make_set_to_run_function(); } @@ -1253,7 +1253,7 @@ TypeState CallOp::propagate_types_internal(const TypeState& input, int arg_count = -1; if (dynamic_string) { - arg_count = dts.get_dynamic_format_arg_count(env.func->guessed_name.to_string(), m_my_idx); + arg_count = dts.get_dynamic_format_arg_count(env.func->name(), m_my_idx); } else if (arg_type.is_constant_string()) { auto& str = arg_type.get_string(); arg_count = dts.get_format_arg_count(str); diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index b70f8d89ed..8003e4f476 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -4956,8 +4956,7 @@ void ConditionalMoveFalseElement::push_to_stack(const Env& env, FormPool& pool, // pop the value and the original auto popped = pop_to_forms({old_value, source}, env, pool, stack, true); if (!is_symbol_true(popped.at(0))) { - lg::warn("{}: Failed to ConditionalMoveFalseElement::push_to_stack", - env.func->guessed_name.to_string()); + lg::warn("{}: Failed to ConditionalMoveFalseElement::push_to_stack", env.func->name()); stack.push_value_to_reg(source, popped.at(1), true, TypeSpec("symbol")); stack.push_form_element(this, true); return; @@ -5139,7 +5138,7 @@ void VectorFloatLoadStoreElement::push_to_stack(const Env& env, FormPool& pool, } } - auto name = env.func->guessed_name.to_string(); + auto name = env.func->name(); // don't find vector-! inside of vector-!. if (!m_is_load && name != "vector-!" && name != "vector+!" && name != "vector-reset!") { if (try_vector_reset_inline(env, pool, stack, this)) { diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index a4e9afd8c6..923aa050f3 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -553,7 +553,7 @@ std::string LinkedObjectFile::print_function_disassembly(Function& func, const std::string& extra_name) { std::string result; result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; - result += "; .function " + func.guessed_name.to_string() + " " + extra_name + "\n"; + result += "; .function " + func.name() + " " + extra_name + "\n"; result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; result += func.prologue.to_string(2) + "\n"; if (func.warnings.has_warnings()) { diff --git a/decompiler/ObjectFile/ObjectFileDB.cpp b/decompiler/ObjectFile/ObjectFileDB.cpp index 0b22731517..9771ac7a40 100644 --- a/decompiler/ObjectFile/ObjectFileDB.cpp +++ b/decompiler/ObjectFile/ObjectFileDB.cpp @@ -681,7 +681,7 @@ void ObjectFileDB::analyze_functions_ir1(const Config& config) { func.guessed_name.unique_id = uid++; func.guessed_name.id_in_object = func_in_obj++; func.guessed_name.object_name = data.to_unique_name(); - auto name = func.guessed_name.to_string(); + auto name = func.name(); if (unique_names.find(name) != unique_names.end()) { duplicated_functions[name].insert(data.to_unique_name()); @@ -700,7 +700,7 @@ void ObjectFileDB::analyze_functions_ir1(const Config& config) { for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { (void)segment_id; - auto name = func.guessed_name.to_string(); + auto name = func.name(); if (duplicated_functions.find(name) != duplicated_functions.end()) { duplicated_functions[name].insert(data.to_unique_name()); diff --git a/decompiler/ObjectFile/ObjectFileDB.h b/decompiler/ObjectFile/ObjectFileDB.h index 519ddc577f..4651551b74 100644 --- a/decompiler/ObjectFile/ObjectFileDB.h +++ b/decompiler/ObjectFile/ObjectFileDB.h @@ -84,6 +84,7 @@ class ObjectFileDB { void ir2_do_segment_analysis_phase1(int seg, const Config& config); void ir2_do_segment_analysis_phase2(int seg, const Config& config); void ir2_setup_labels(const Config& config); + void ir2_run_mips2c(const Config& config); std::string ir2_to_file(ObjectFileData& data, const Config& config); std::string ir2_function_to_string(ObjectFileData& data, Function& function, int seg); std::string ir2_final_out(ObjectFileData& data, diff --git a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp index 3fba1d2cf0..ec6869651d 100644 --- a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp +++ b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp @@ -3,7 +3,7 @@ * This runs the IR2 analysis passes. */ -#include +#include "common/link_types.h" #include "ObjectFileDB.h" #include "common/log/log.h" #include "common/util/Timer.h" @@ -24,6 +24,7 @@ #include "decompiler/analysis/symbol_def_map.h" #include "common/goos/PrettyPrinter.h" #include "decompiler/IR2/Form.h" +#include "decompiler/analysis/mips2c.h" namespace decompiler { @@ -65,17 +66,15 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir, ir2_symbol_definition_map(output_dir); } - // if (!skip_debug_output) { - // lg::info("Storing temporary form result..."); - // ir2_store_current_forms(); - // } - lg::info("Inserting anonymous function definitions..."); ir2_insert_anonymous_functions(DEBUG_SEGMENT); ir2_insert_anonymous_functions(MAIN_SEGMENT); ir2_insert_anonymous_functions(TOP_LEVEL_SEGMENT); + // doesn't really matter where we do this. + ir2_run_mips2c(config); + if (!output_dir.empty()) { lg::info("Writing results..."); ir2_write_results(output_dir, config); @@ -83,7 +82,7 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir, } void ObjectFileDB::ir2_do_segment_analysis_phase1(int seg, const Config& config) { - lg::info("COMMON ANALYSIS 1 {}", seg); + lg::info("ASM analysis for {} segment", SEGMENT_NAMES[seg]); lg::info("Processing basic blocks and control flow graph..."); ir2_basic_block_pass(seg, config); @@ -94,7 +93,7 @@ void ObjectFileDB::ir2_do_segment_analysis_phase1(int seg, const Config& config) } void ObjectFileDB::ir2_do_segment_analysis_phase2(int seg, const Config& config) { - lg::info("COMMON ANALYSIS 2 {}", seg); + lg::info("GOAL analysis for {} segment", SEGMENT_NAMES[seg]); lg::info("Running type analysis..."); ir2_type_analysis_pass(seg, config); @@ -133,6 +132,15 @@ void ObjectFileDB::ir2_setup_labels(const Config& config) { }); } +void ObjectFileDB::ir2_run_mips2c(const Config& config) { + for_each_function_def_order([&](Function& func, int, ObjectFileData&) { + if (config.hacks.mips2c_functions_by_name.count(func.name())) { + lg::info("MIPS2C on {}", func.name()); + run_mips2c(&func); + } + }); +} + /*! * Analyze the top level function of each object. * - Find global function definitions @@ -174,7 +182,7 @@ void ObjectFileDB::ir2_top_level_pass(const Config& config) { func.guessed_name.unique_id = uid++; func.guessed_name.id_in_object = func_in_obj++; func.guessed_name.object_name = data.to_unique_name(); - auto name = func.guessed_name.to_string(); + auto name = func.name(); switch (func.guessed_name.kind) { case FunctionName::FunctionKind::METHOD: @@ -212,6 +220,12 @@ void ObjectFileDB::ir2_top_level_pass(const Config& config) { func.warnings.info("Flagged as asm by config"); func.suspected_asm = true; } + + if (config.hacks.mips2c_functions_by_name.find(name) != + config.hacks.mips2c_functions_by_name.end()) { + func.warnings.info("Flagged as mips2c by config"); + func.suspected_asm = true; + } } } }); @@ -219,7 +233,7 @@ void ObjectFileDB::ir2_top_level_pass(const Config& config) { // we remember duplicates like this so we can warn on all occurances of the duplicate name for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { (void)segment_id; - auto name = func.guessed_name.to_string(); + auto name = func.name(); if (duplicated_functions.find(name) != duplicated_functions.end()) { duplicated_functions[name].insert(data.to_unique_name()); @@ -280,23 +294,21 @@ void ObjectFileDB::ir2_basic_block_pass(int seg, const Config& config) { // build a control flow graph, just looking at branch instructions. CondWithElseLengthHack hack; - auto lookup = - config.hacks.cond_with_else_len_by_func_name.find(func.guessed_name.to_string()); + auto lookup = config.hacks.cond_with_else_len_by_func_name.find(func.name()); if (lookup != config.hacks.cond_with_else_len_by_func_name.end()) { hack = lookup->second; } std::unordered_set asm_br_blocks; - auto asm_lookup = - config.hacks.blocks_ending_in_asm_branch_by_func_name.find(func.guessed_name.to_string()); + auto asm_lookup = config.hacks.blocks_ending_in_asm_branch_by_func_name.find(func.name()); if (asm_lookup != config.hacks.blocks_ending_in_asm_branch_by_func_name.end()) { asm_br_blocks = asm_lookup->second; } func.cfg = build_cfg(data.linked_data, seg, func, hack, asm_br_blocks); if (!func.cfg->is_fully_resolved()) { - lg::warn("Function {} from {} failed to build control flow graph!", - func.guessed_name.to_string(), data.to_unique_name()); + lg::warn("Function {} from {} failed to build control flow graph!", func.name(), + data.to_unique_name()); failed_to_build_cfg++; } else { func.cfg_ok = true; @@ -363,13 +375,12 @@ void ObjectFileDB::ir2_atomic_op_pass(int seg, const Config& config) { func.ir2.atomic_ops_attempted = true; attempted++; try { - bool inline_asm = - config.hacks.hint_inline_assembly_functions.find(func.guessed_name.to_string()) != - config.hacks.hint_inline_assembly_functions.end(); + bool inline_asm = config.hacks.hint_inline_assembly_functions.find(func.name()) != + config.hacks.hint_inline_assembly_functions.end(); std::unordered_set blocks_ending_in_asm_branch; - auto asm_branch_it = config.hacks.blocks_ending_in_asm_branch_by_func_name.find( - func.guessed_name.to_string()); + auto asm_branch_it = + config.hacks.blocks_ending_in_asm_branch_by_func_name.find(func.name()); if (asm_branch_it != config.hacks.blocks_ending_in_asm_branch_by_func_name.end()) { blocks_ending_in_asm_branch = asm_branch_it->second; @@ -382,8 +393,8 @@ void ObjectFileDB::ir2_atomic_op_pass(int seg, const Config& config) { func.ir2.env.set_end_var(func.ir2.atomic_ops->end_op().return_var()); successful++; } catch (std::exception& e) { - lg::warn("Function {} from {} could not be converted to atomic ops: {}", - func.guessed_name.to_string(), data.to_unique_name(), e.what()); + lg::warn("Function {} from {} could not be converted to atomic ops: {}", func.name(), + data.to_unique_name(), e.what()); func.warnings.general_warning("Failed to convert to atomic ops: {}", e.what()); } } @@ -440,7 +451,7 @@ void ObjectFileDB::ir2_type_analysis_pass(int seg, const Config& config) { func.type = ts; attempted_functions++; // try type analysis here. - auto func_name = func.guessed_name.to_string(); + auto func_name = func.name(); auto register_casts = try_lookup(config.register_type_casts_by_function_by_atomic_op_idx, func_name); func.ir2.env.set_type_casts(register_casts); @@ -465,9 +476,8 @@ void ObjectFileDB::ir2_type_analysis_pass(int seg, const Config& config) { func.warnings.type_prop_warning("Type analysis failed"); } } else { - lg::warn("Function {} didn't know its type", func.guessed_name.to_string()); - func.warnings.type_prop_warning("Function {} has unknown type", - func.guessed_name.to_string()); + lg::warn("Function {} didn't know its type", func.name()); + func.warnings.type_prop_warning("Function {} has unknown type", func.name()); } } }); @@ -505,7 +515,7 @@ void ObjectFileDB::ir2_register_usage_pass(int seg) { for (auto& x : dep_regs) { if ((x.get_kind() == Reg::VF && x.get_vf() != 0) || x.get_kind() == Reg::SPECIAL) { - lg::error("Bad vf dependency on {} in {}", x.to_charp(), func.guessed_name.to_string()); + lg::error("Bad vf dependency on {} in {}", x.to_charp(), func.name()); func.warnings.bad_vf_dependency("{}", x.to_string()); continue; } @@ -519,8 +529,7 @@ void ObjectFileDB::ir2_register_usage_pass(int seg) { continue; } - lg::error("Bad register dependency on {} in {}", x.to_charp(), - func.guessed_name.to_string()); + lg::error("Bad register dependency on {} in {}", x.to_charp(), func.name()); func.warnings.general_warning("Function may read a register that is not set: {}", x.to_string()); } @@ -548,7 +557,7 @@ void ObjectFileDB::ir2_variable_pass(int seg) { func.ir2.env.set_local_vars(*result); } } catch (const std::exception& e) { - lg::warn("variable pass failed on {}: {}", func.guessed_name.to_string(), e.what()); + lg::warn("variable pass failed on {}: {}", func.name(), e.what()); } } }); @@ -610,7 +619,7 @@ void ObjectFileDB::ir2_build_expressions(int seg, const Config& config) { if (func.ir2.top_form && func.ir2.env.has_type_analysis() && func.ir2.env.has_local_vars() && func.ir2.env.types_succeeded) { attempted++; - auto name = func.guessed_name.to_string(); + auto name = func.name(); auto arg_config = config.function_arg_names.find(name); auto var_config = config.function_var_overrides.find(name); if (convert_to_expressions(func.ir2.top_form, *func.ir2.form_pool, func, @@ -685,7 +694,7 @@ void ObjectFileDB::ir2_insert_anonymous_functions(int seg) { total += insert_static_refs(func.ir2.top_form, *func.ir2.form_pool, func, dts); } catch (std::exception& e) { func.warnings.general_warning("Failed static ref finding: {}\n", e.what()); - lg::error("Function {} failed static ref: {}\n", func.guessed_name.to_string(), e.what()); + lg::error("Function {} failed static ref: {}\n", func.name(), e.what()); } } }); @@ -733,7 +742,7 @@ std::string ObjectFileDB::ir2_to_file(ObjectFileData& data, const Config& config result += ir2_function_to_string(data, func, seg); } catch (std::exception& e) { result += "Failed to write"; - result += func.guessed_name.to_string(); + result += func.name(); result += ": "; result += e.what(); result += "\n"; @@ -856,7 +865,7 @@ void append_commented(std::string& line, std::string ObjectFileDB::ir2_function_to_string(ObjectFileData& data, Function& func, int seg) { std::string result; result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; - result += "; .function " + func.guessed_name.to_string() + "\n"; + result += "; .function " + func.name() + "\n"; result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; result += func.prologue.to_string(2) + "\n"; if (func.warnings.has_warnings()) { @@ -994,6 +1003,10 @@ std::string ObjectFileDB::ir2_function_to_string(ObjectFileData& data, Function& } } + if (func.mips2c_output) { + result += *func.mips2c_output; + } + result += "\n"; assert(total_instructions_printed == (func.end_word - func.start_word - 1)); diff --git a/decompiler/analysis/.clang-format b/decompiler/analysis/.clang-format new file mode 100644 index 0000000000..b6ac88dd5a --- /dev/null +++ b/decompiler/analysis/.clang-format @@ -0,0 +1,4 @@ +--- +BasedOnStyle: Chromium +ColumnLimit: 100 +SortIncludes: true diff --git a/decompiler/analysis/atomic_op_builder.cpp b/decompiler/analysis/atomic_op_builder.cpp index 63b81b90a4..3d591eacfa 100644 --- a/decompiler/analysis/atomic_op_builder.cpp +++ b/decompiler/analysis/atomic_op_builder.cpp @@ -1,11 +1,12 @@ #include + +#include "atomic_op_builder.h" #include "common/log/log.h" #include "common/symbols.h" -#include "atomic_op_builder.h" -#include "decompiler/Function/Function.h" #include "decompiler/Disasm/InstructionMatching.h" -#include "decompiler/util/TP_Type.h" +#include "decompiler/Function/Function.h" #include "decompiler/Function/Warnings.h" +#include "decompiler/util/TP_Type.h" namespace decompiler { diff --git a/decompiler/analysis/atomic_op_builder.h b/decompiler/analysis/atomic_op_builder.h index 9b2281efcd..cc72770da4 100644 --- a/decompiler/analysis/atomic_op_builder.h +++ b/decompiler/analysis/atomic_op_builder.h @@ -1,5 +1,6 @@ #pragma once #include + #include "decompiler/IR2/AtomicOp.h" namespace decompiler { diff --git a/decompiler/analysis/cfg_builder.cpp b/decompiler/analysis/cfg_builder.cpp index 7054f36787..40b9663440 100644 --- a/decompiler/analysis/cfg_builder.cpp +++ b/decompiler/analysis/cfg_builder.cpp @@ -4,9 +4,9 @@ */ #include "cfg_builder.h" -#include "decompiler/util/MatchParam.h" #include "decompiler/Function/Function.h" #include "decompiler/IR2/Form.h" +#include "decompiler/util/MatchParam.h" namespace decompiler { namespace { @@ -395,8 +395,8 @@ bool try_clean_up_sc_as_and(FormPool& pool, Function& func, ShortCircuitElement* bool this_live_out = (branch_info.written_and_unused.find(ir_dest.reg()) == branch_info.written_and_unused.end()); if (live_out_result != this_live_out) { - lg::error("Bad live out result on {}. At 0 was {} now at {} is {}", - func.guessed_name.to_string(), live_out_result, i, this_live_out); + lg::error("Bad live out result on {}. At 0 was {} now at {} is {}", func.name(), + live_out_result, i, this_live_out); } assert(live_out_result == this_live_out); } @@ -677,7 +677,7 @@ void convert_cond_no_else_to_compare(FormPool& pool, if (condition_as_single) { *ir_loc = replacement; } else { - // lg::error("Weird case in {}", f.guessed_name.to_string()); + // lg::error("Weird case in {}", f.name()); (void)f; auto seq = cne->entries.front().condition; seq->pop_back(); @@ -1785,8 +1785,7 @@ void build_initial_forms(Function& function) { function.ir2.top_form = result; } catch (std::runtime_error& e) { function.warnings.general_warning(e.what()); - lg::warn("Failed to build initial forms in {}: {}", function.guessed_name.to_string(), - e.what()); + lg::warn("Failed to build initial forms in {}: {}", function.name(), e.what()); } } } // namespace decompiler diff --git a/decompiler/analysis/expression_build.cpp b/decompiler/analysis/expression_build.cpp index 4cc5d1575f..082b715049 100644 --- a/decompiler/analysis/expression_build.cpp +++ b/decompiler/analysis/expression_build.cpp @@ -1,9 +1,9 @@ #include "expression_build.h" +#include "common/goos/PrettyPrinter.h" #include "decompiler/Function/Function.h" #include "decompiler/IR2/Form.h" #include "decompiler/IR2/FormStack.h" #include "decompiler/util/DecompilerTypeSystem.h" -#include "common/goos/PrettyPrinter.h" namespace decompiler { @@ -157,15 +157,15 @@ bool convert_to_expressions( auto warn = fmt::format( "Function {} has a return type of none, but the expression builder found a return " "statement.", - f.guessed_name.to_string()); + f.name()); f.warnings.expression_build_warning(warn); lg::warn(warn); } } } catch (std::exception& e) { - f.warnings.expression_build_warning("In {}: {}", f.guessed_name.to_string(), e.what()); - lg::warn("In {}: {}", f.guessed_name.to_string(), e.what()); + f.warnings.expression_build_warning("In {}: {}", f.name(), e.what()); + lg::warn("In {}: {}", f.name(), e.what()); return false; } diff --git a/decompiler/analysis/expression_build.h b/decompiler/analysis/expression_build.h index 88b01c026d..fadafa60a3 100644 --- a/decompiler/analysis/expression_build.h +++ b/decompiler/analysis/expression_build.h @@ -1,8 +1,8 @@ #pragma once +#include #include #include -#include namespace decompiler { class Form; diff --git a/decompiler/analysis/final_output.cpp b/decompiler/analysis/final_output.cpp index 15a825b665..c7e4077786 100644 --- a/decompiler/analysis/final_output.cpp +++ b/decompiler/analysis/final_output.cpp @@ -1,17 +1,16 @@ -#include "decompiler/IR2/GenericElementMatcher.h" #include "final_output.h" -#include "decompiler/IR2/Form.h" #include "common/goos/PrettyPrinter.h" -#include "decompiler/util/DecompilerTypeSystem.h" +#include "decompiler/IR2/Form.h" +#include "decompiler/IR2/GenericElementMatcher.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" +#include "decompiler/util/DecompilerTypeSystem.h" namespace decompiler { goos::Object get_arg_list_for_function(const Function& func, const Env& env) { std::vector argument_elts; if (func.type.arg_count() < 1) { - throw std::runtime_error( - fmt::format("Function {} has unknown type.\n", func.guessed_name.to_string())); + throw std::runtime_error(fmt::format("Function {} has unknown type.\n", func.name())); } assert(func.type.arg_count() >= 1); for (size_t i = 0; i < func.type.arg_count() - 1; i++) { @@ -105,9 +104,9 @@ std::string final_defun_out(const Function& func, top.push_back(pretty_print::to_symbol(def_name)); if (behavior) { - top.push_back(pretty_print::to_symbol(func.guessed_name.to_string() + " " + *behavior)); + top.push_back(pretty_print::to_symbol(func.name() + " " + *behavior)); } else { - top.push_back(pretty_print::to_symbol(func.guessed_name.to_string())); + top.push_back(pretty_print::to_symbol(func.name())); } top.push_back(arguments); auto top_form = pretty_print::build_list(top); @@ -147,7 +146,7 @@ std::string final_defun_out(const Function& func, assert(special_mode == FunctionDefSpecials::NONE); std::vector top; top.push_back(pretty_print::to_symbol(def_name)); - top.push_back(pretty_print::to_symbol(func.guessed_name.to_string())); + top.push_back(pretty_print::to_symbol(func.name())); top.push_back(arguments); auto top_form = pretty_print::build_list(top); @@ -160,7 +159,7 @@ std::string final_defun_out(const Function& func, std::vector top; top.push_back(pretty_print::to_symbol("state-handler")); - top.push_back(pretty_print::to_symbol(func.guessed_name.to_string())); + top.push_back(pretty_print::to_symbol(func.name())); top.push_back(arguments); auto top_form = pretty_print::build_list(top); @@ -334,7 +333,7 @@ std::string write_from_top_level_form(Form* top_form, something_matched = true; result += fmt::format(";; definition for function {}\n", global_match_result.maps.strings.at(func_name)); - if (skip_functions.find(func->guessed_name.to_string()) == skip_functions.end()) { + if (skip_functions.find(func->name()) == skip_functions.end()) { result += careful_function_to_string(func, dts); } else { result += ";; skipped.\n\n"; @@ -351,7 +350,7 @@ std::string write_from_top_level_form(Form* top_form, result += fmt::format(";; definition for method {} of type {}\n", func->guessed_name.method_id, method_match_result.maps.strings.at(type_name)); - if (skip_functions.find(func->guessed_name.to_string()) == skip_functions.end()) { + if (skip_functions.find(func->name()) == skip_functions.end()) { result += careful_function_to_string(func, dts); } else { result += ";; skipped.\n\n"; @@ -387,7 +386,7 @@ std::string write_from_top_level_form(Form* top_form, something_matched = true; result += fmt::format(";; definition (debug) for function {}\n", debug_match_result.maps.strings.at(0)); - if (skip_functions.find(func->guessed_name.to_string()) == skip_functions.end()) { + if (skip_functions.find(func->name()) == skip_functions.end()) { result += careful_function_to_string(func, dts, FunctionDefSpecials::DEFUN_DEBUG); } else { result += ";; skipped.\n\n"; diff --git a/decompiler/analysis/final_output.h b/decompiler/analysis/final_output.h index dd9031a9e7..a414ed29b5 100644 --- a/decompiler/analysis/final_output.h +++ b/decompiler/analysis/final_output.h @@ -1,5 +1,6 @@ #pragma once #include + #include "decompiler/Function/Function.h" namespace decompiler { diff --git a/decompiler/analysis/find_defstates.cpp b/decompiler/analysis/find_defstates.cpp index a44f7b60f6..ae5b192c3b 100644 --- a/decompiler/analysis/find_defstates.cpp +++ b/decompiler/analysis/find_defstates.cpp @@ -1,11 +1,11 @@ #include "find_defstates.h" -#include "decompiler/IR2/Form.h" #include "common/goos/PrettyPrinter.h" +#include "common/type_system/state.h" +#include "decompiler/IR2/Form.h" #include "decompiler/IR2/GenericElementMatcher.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" -#include "common/type_system/state.h" namespace decompiler { @@ -129,7 +129,7 @@ std::vector get_defstate_entries( this_entry.is_behavior = true; if (print_renames) { - fmt::print("RENAME: {} to ", handler_func->guessed_name.to_string()); + fmt::print("RENAME: {} to ", handler_func->name()); } if (virtual_child) { @@ -138,7 +138,7 @@ std::vector get_defstate_entries( handler_func->guessed_name.set_as_nv_state(state_name, handler_kind); } if (print_renames) { - fmt::print("{}\n", handler_func->guessed_name.to_string()); + fmt::print("{}\n", handler_func->name()); } // scary part - modify the function type! diff --git a/decompiler/analysis/find_defstates.h b/decompiler/analysis/find_defstates.h index 925795965e..85f29818fd 100644 --- a/decompiler/analysis/find_defstates.h +++ b/decompiler/analysis/find_defstates.h @@ -1,7 +1,7 @@ #pragma once -#include "decompiler/util/DecompilerTypeSystem.h" #include "decompiler/Function/Function.h" +#include "decompiler/util/DecompilerTypeSystem.h" namespace decompiler { void run_defstate(Function& top_level_func); diff --git a/decompiler/analysis/inline_asm_rewrite.cpp b/decompiler/analysis/inline_asm_rewrite.cpp index ded4bc5c82..3597b07cfb 100644 --- a/decompiler/analysis/inline_asm_rewrite.cpp +++ b/decompiler/analysis/inline_asm_rewrite.cpp @@ -1,13 +1,12 @@ #include "inline_asm_rewrite.h" - +#include "common/goos/PrettyPrinter.h" #include "decompiler/Function/Function.h" #include "decompiler/IR2/Form.h" #include "decompiler/IR2/FormStack.h" -#include "decompiler/util/DecompilerTypeSystem.h" -#include "common/goos/PrettyPrinter.h" #include "decompiler/IR2/OpenGoalMapping.h" -#include "decompiler/analysis/reg_usage.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" +#include "decompiler/analysis/reg_usage.h" +#include "decompiler/util/DecompilerTypeSystem.h" namespace decompiler { @@ -89,8 +88,8 @@ bool rewrite_inline_asm_instructions(Form* top_level_form, top_level_form->push_back(rlet); } } catch (std::exception& e) { - std::string warning = fmt::format("ASM instruction re-writing failed in {}: {}", - f.guessed_name.to_string(), e.what()); + std::string warning = + fmt::format("ASM instruction re-writing failed in {}: {}", f.name(), e.what()); lg::warn(warning); f.warnings.general_warning(";; {}", warning); return false; diff --git a/decompiler/analysis/insert_lets.cpp b/decompiler/analysis/insert_lets.cpp index 0b72781275..77c45a63e4 100644 --- a/decompiler/analysis/insert_lets.cpp +++ b/decompiler/analysis/insert_lets.cpp @@ -1,10 +1,10 @@ #include -#include #include +#include -#include "insert_lets.h" #include "decompiler/IR2/GenericElementMatcher.h" #include "decompiler/util/DecompilerTypeSystem.h" +#include "insert_lets.h" namespace decompiler { @@ -79,8 +79,7 @@ Form* lca_form(Form* a, Form* b, const Env& env) { bi--; } if (!result) { - fmt::print("{} bad form is {}\n\n{}\n", env.func->guessed_name.to_string(), a->to_string(env), - b->to_string(env)); + fmt::print("{} bad form is {}\n\n{}\n", env.func->name(), a->to_string(env), b->to_string(env)); } assert(result); @@ -733,7 +732,7 @@ FormElement* rewrite_multi_let_as_vector_dot(LetElement* in, const Env& env, For } // don't inline in the actual function... - if (env.func->guessed_name.to_string() == "vector-dot") { + if (env.func->name() == "vector-dot") { return nullptr; } @@ -804,7 +803,7 @@ bool register_can_hold_var(const Register& reg) { LetStats insert_lets(const Function& func, Env& env, FormPool& pool, Form* top_level_form) { (void)func; - // if (func.guessed_name.to_string() != "(method 4 pair)") { + // if (func.name() != "(method 4 pair)") { // return {}; // } LetStats stats; diff --git a/decompiler/analysis/insert_lets.h b/decompiler/analysis/insert_lets.h index 64e6367719..226186fc75 100644 --- a/decompiler/analysis/insert_lets.h +++ b/decompiler/analysis/insert_lets.h @@ -1,7 +1,7 @@ #pragma once -#include "decompiler/IR2/Env.h" #include "decompiler/Function/Function.h" +#include "decompiler/IR2/Env.h" #include "decompiler/IR2/Form.h" namespace decompiler { diff --git a/decompiler/analysis/label_types.cpp b/decompiler/analysis/label_types.cpp index ca60e5f403..cc9399151d 100644 --- a/decompiler/analysis/label_types.cpp +++ b/decompiler/analysis/label_types.cpp @@ -56,7 +56,7 @@ void find_functions(LabelDB* db, LinkedObjectFile* file) { auto idx_of_label = db->try_get_index_by_offset(seg, offset_of_function); if (!idx_of_label) { func.warnings.general_warning("Could not find any references to this function: {}", - func.guessed_name.to_string()); + func.name()); } else { auto old = db->set_and_get_previous(*idx_of_label, func.type, false, {}); if (old.known) { diff --git a/decompiler/analysis/mips2c.cpp b/decompiler/analysis/mips2c.cpp new file mode 100644 index 0000000000..17a9892304 --- /dev/null +++ b/decompiler/analysis/mips2c.cpp @@ -0,0 +1,779 @@ + +#include + +#include "mips2c.h" + +#include "decompiler/Disasm/InstructionMatching.h" +#include "decompiler/Function/Function.h" +#include "decompiler/ObjectFile/LinkedObjectFile.h" + +namespace decompiler { + +////////////////////// +// Register Helpers +////////////////////// + +Register rs7() { + return make_gpr(Reg::S7); +} + +Register rr0() { + return make_gpr(Reg::R0); +} + +Register rfp() { + return make_gpr(Reg::FP); +} + +Register rra() { + return make_gpr(Reg::RA); +} + +Register rt9() { + return make_gpr(Reg::T9); +} + +Register rv0() { + return make_gpr(Reg::V0); +} + +Register rsp() { + return make_gpr(Reg::SP); +} + +Register make_vf(int idx) { + return Register(Reg::VF, idx); +} + +std::string goal_to_c_name(const std::string& name) { + std::string result; + for (auto c : name) { + if (c == '!' || c == '?' || c == '*') { + continue; + } + + if (c == '-') { + c = '_'; + } + + result.push_back(c); + } + return result; +} + +std::string goal_to_c_function_name(const FunctionName& name) { + switch (name.kind) { + case FunctionName::FunctionKind::GLOBAL: + return goal_to_c_name(name.function_name); + default: + assert(false); + } +} + +const char* reg_to_name(const InstructionAtom& atom) { + assert(atom.is_reg()); + return atom.get_reg().to_charp(); +} + +struct Mips2C_Line { + std::string code; + std::string comment; + + Mips2C_Line() = default; + Mips2C_Line(const std::string& _code) : code(_code) {} + Mips2C_Line(const std::string& _code, const std::string& _comment) + : code(_code), comment(_comment) {} +}; + +struct Mips2C_Output { + void output_label(int block_idx) { lines.push_back(fmt::format("\nblock_{}:", block_idx)); } + void output_line_comment(const std::string& text) { lines.emplace_back("// " + text); } + + void output_instr(const std::string& instr, const std::string& comment) { + lines.emplace_back(instr, comment); + } + + std::string write_to_string(const FunctionName& goal_func_name) const { + std::string name = goal_to_c_function_name(goal_func_name); + std::string result = "//--------------------------MIPS2C---------------------\n"; + result += "#include \"game/mips2c/mips2c_private.h\"\n"; + result += "#include \"game/kernel/kscheme.h\"\n"; + + // start of namespace for this function + result += "namespace Mips2C {\n"; + result += fmt::format("namespace {} {{\n", name); + + if (!symbol_cache.empty()) { + result += "struct Cache {\n"; + for (auto& sym : symbol_cache) { + result += fmt::format(" void* {}; // {}\n", goal_to_c_name(sym), sym); + } + result += "} cache;\n\n"; + } + + result += "u64 execute(void* ctxt) {\n"; + result += " auto* c = (ExecutionContext*)ctxt;\n"; + result += " bool bc = false;"; + for (auto& line : lines) { + result += " "; + result += line.code; + if (!line.comment.empty()) { + if (line.code.length() < 50) { + for (int i = 0; i < 50 - (int)line.code.length(); i++) { + result.push_back(' '); + } + } + result.append("// "); + result.append(line.comment); + } + + result += '\n'; + } + + result += "end_of_function:\n return c->gprs[v0].du64[0];\n"; + result += "}\n\n"; + + // link function: + result += "void link() {\n"; + for (auto& sym : symbol_cache) { + result += fmt::format(" cache.{} = intern_from_c(\"{}\").c();\n", goal_to_c_name(sym), sym); + } + result += + fmt::format(" gLinkedFunctionTable.reg(\"{}\", execute);\n", goal_func_name.to_string()); + result += "}\n\n"; + + result += fmt::format("}} // namespace {}\n", name); + result += "} // namespace Mips2C\n"; + + result += + fmt::format("// add {}::link to the link callback table for the object file.\n", name); + result += "// FWD DEC:\n"; + result += fmt::format("namespace {} {{ extern void link(); }}\n", name); + return result; + } + + void require_symbol(const std::string& name) { symbol_cache.insert(name); } + + std::vector lines; + std::set symbol_cache; +}; + +struct M2C_Block { + int idx = -1; + int succ_branch = -1; + int succ_ft = -1; + std::vector pred; + + int start_instr = -1; + int end_instr = -1; + + bool has_branch = false; + bool branch_likely = false; + bool branch_always = false; + + bool has_pred(int pidx) const { + for (auto p : pred) { + if (p == pidx) { + return true; + } + } + return false; + } +}; + +void link_fall_through(int first_idx, int second_idx, std::vector& blocks) { + auto& first = blocks.at(first_idx); + auto& second = blocks.at(second_idx); + + assert(first.succ_ft == -1); // don't want to overwrite something by accident. + // can only fall through to the next code in memory. + assert(first_idx + 1 == second_idx); + first.succ_ft = second_idx; + + if (!second.has_pred(first_idx)) { + // if a block can block fall through and branch to the same block, we want to avoid adding + // it as a pred twice. This is rare, but does happen and makes sense with likely branches + // which only run the delay slot when taken. + second.pred.push_back(first_idx); + } +} + +void link_branch(int first_idx, int second_idx, std::vector& blocks) { + auto& first = blocks.at(first_idx); + auto& second = blocks.at(second_idx); + assert(first.succ_branch == -1); + first.succ_branch = second_idx; + + if (!second.has_pred(first_idx)) { + // see comment in link_fall_through + second.pred.push_back(first_idx); + } +} + +void link_fall_through_likely(int first_idx, int second_idx, std::vector& blocks) { + auto& first = blocks.at(first_idx); + auto& second = blocks.at(second_idx); + assert(first.succ_ft == -1); // don't want to overwrite something by accident. + // can only fall through to the next code in memory. + assert(first_idx + 2 == second_idx); + + first.succ_ft = second_idx; + + if (!second.has_pred(first_idx)) { + // see comment in link_fall_through + second.pred.push_back(first_idx); + } +} + +/*! + * Compute predecessor and successor of each block + * + * NOTE: due to likely branch delays/etc, succ_ft and succ_branch behave strangely. + * + * The succ_ft destination is always taken if has_branch is false. + * The succ_branch destination is always taken if branch_always it true. + * + * Otherwise, has_branch is true, and the succ_branch is taken if the branch condition is true. + * The succ_ft and succ_branch may be _anywhere_. succ_ft may not always be the next destination. + */ +std::vector setup_preds_and_succs(const Function& func, const LinkedObjectFile& file) { + // create m2c blocks + std::vector blocks; + blocks.resize(func.basic_blocks.size()); + for (size_t i = 0; i < blocks.size(); i++) { + blocks[i].idx = i; + blocks[i].start_instr = func.basic_blocks[i].start_word; + blocks[i].end_instr = func.basic_blocks[i].end_word; + } + + // set up succ / pred + for (int i = 0; i < int(func.basic_blocks.size()); i++) { + auto& b = func.basic_blocks[i]; + assert(!blocks.at(i).branch_always); + bool not_last = (i + 1) < int(func.basic_blocks.size()); + + if (b.end_word == b.start_word) { + // there's no room for a branch here, fall through to the end + if (not_last) { + link_fall_through(i, i + 1, blocks); + } + } else { + // room for at least a likely branch, try that first. + int likely_branch_idx = b.end_word - 1; + assert(likely_branch_idx >= b.start_word); + auto& likely_branch_candidate = func.instructions.at(likely_branch_idx); + + if (is_branch(likely_branch_candidate, true)) { + // is a likely branch + blocks.at(i).has_branch = true; + blocks.at(i).branch_likely = true; + // blocks.at(i).kind = CfgVtx::DelaySlotKind::NO_DELAY; + bool branch_always = is_always_branch(likely_branch_candidate); + + // need to find block target + int block_target = -1; + int label_target = likely_branch_candidate.get_label_target(); + assert(label_target != -1); + const auto& label = file.labels.at(label_target); + // assert(label.target_segment == seg); + assert((label.offset % 4) == 0); + int offset = label.offset / 4 - func.start_word; + assert(offset >= 0); + for (int j = int(func.basic_blocks.size()); j-- > 0;) { + if (func.basic_blocks[j].start_word == offset) { + block_target = j; + break; + } + } + + assert(block_target != -1); + // "branch" to delay slot, which then "falls through" to the destination. + link_branch(i, i + 1, blocks); + + if (branch_always) { + // don't continue to the next one + blocks.at(i).branch_always = true; + } else { + // not an always branch + if (not_last) { + // "fall through" to after the delay slot block. + // don't take the delay slot. + link_fall_through_likely(i, i + 2, blocks); + } + } + + auto& delay_block = blocks.at(i + 1); + delay_block.branch_likely = false; + delay_block.branch_always = true; + delay_block.has_branch = true; + // delay_block.kind = CfgVtx::DelaySlotKind::NO_DELAY; + link_branch(i + 1, block_target, blocks); + + } else { + if (b.end_word - b.start_word < 2) { + // no room for a branch, just fall through + if (not_last) { + link_fall_through(i, i + 1, blocks); + } + } else { + // try as a normal branch. + int idx = b.end_word - 2; + assert(idx >= b.start_word); + auto& branch_candidate = func.instructions.at(idx); + // auto& delay_slot_candidate = func.instructions.at(idx + 1); + if (is_branch(branch_candidate, false)) { + blocks.at(i).has_branch = true; + blocks.at(i).branch_likely = false; + // blocks.at(i).kind = get_delay_slot(delay_slot_candidate); + bool branch_always = is_always_branch(branch_candidate); + + // need to find block target + int block_target = -1; + int label_target = branch_candidate.get_label_target(); + assert(label_target != -1); + const auto& label = file.labels.at(label_target); + // assert(label.target_segment == seg); + assert((label.offset % 4) == 0); + int offset = label.offset / 4 - func.start_word; + assert(offset >= 0); + + // the order here matters when there are zero size blocks. Unclear what the best answer + // is. + // i think in end it doesn't actually matter?? + // for (int j = 0; j < int(func.basic_blocks.size()); j++) { + for (int j = int(func.basic_blocks.size()); j-- > 0;) { + if (func.basic_blocks[j].start_word == offset) { + block_target = j; + break; + } + } + + assert(block_target != -1); + link_branch(i, block_target, blocks); + + if (branch_always) { + // don't continue to the next one + blocks.at(i).branch_always = true; + } else { + // not an always branch + if (not_last) { + link_fall_through(i, i + 1, blocks); + } + } + } else { + // not a branch. + if (not_last) { + link_fall_through(i, i + 1, blocks); + } + } + } + } + } + } + return blocks; +} + +bool block_requires_label(const Function* f, + const std::vector& blocks, + size_t block_idx) { + const auto& block = blocks[block_idx]; + if (block.pred.empty()) { + // no way to get to this block?? + if (block_idx != 0) { + // don't warn on the first block. + lg::warn("Mips2C function {} block {} is unreachable.", f->name(), block_idx); + } + return false; + } + + if (block.pred.size() == 1 && block_idx > 0 && block.pred.front() == (int)block_idx - 1 && + blocks.at(block_idx - 1).succ_ft == (int)block_idx) { + // the only way to get to this block is to fall through. + return false; + } + + return true; +} + +int g_unknown = 0; +Mips2C_Line handle_unknown(const std::string& instr_str) { + g_unknown++; + return fmt::format("// Unknown instr: {}", instr_str); +} + +Mips2C_Line handle_generic_load(const Instruction& i0, const std::string& instr_str) { + if (i0.get_src(1).is_reg(rsp())) { + return handle_unknown(instr_str); + } else { + return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_dst(0)), + i0.get_src(0).get_imm(), reg_to_name(i0.get_src(1))), + instr_str}; + } +} + +Mips2C_Line handle_lw(Mips2C_Output& out, const Instruction& i0, const std::string& instr_str) { + if (i0.get_src(1).is_reg(rs7()) && i0.get_src(0).is_sym()) { + // symbol load. + out.require_symbol(i0.get_src(0).get_sym()); + return {fmt::format("c->load_symbol({}, cache.{});", reg_to_name(i0.get_dst(0)), + goal_to_c_name(i0.get_src(0).get_sym())), + instr_str}; + + } else { + // fall back to standard loads + return handle_generic_load(i0, instr_str); + } +} + +Mips2C_Line handle_generic_store(Mips2C_Output& /*out*/, + const Instruction& i0, + const std::string& instr_str) { + if (i0.get_src(2).is_reg(Register(Reg::GPR, Reg::SP))) { + return handle_unknown(instr_str); + } else { + return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_src(0)), + i0.get_src(1).get_imm(), reg_to_name(i0.get_src(2))), + instr_str}; + } +} + +Mips2C_Line handle_generic_op2_u16(const Instruction& i0, const std::string& instr_str) { + return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_dst(0)), + reg_to_name(i0.get_src(0)), i0.get_src(1).get_imm()), + instr_str}; +} + +Mips2C_Line handle_sw(Mips2C_Output& out, const Instruction& i0, const std::string& instr_str) { + if (i0.get_src(1).is_sym() && i0.get_src(2).is_reg(rs7())) { + return handle_unknown(instr_str); + // auto name = i0.get_src(1).get_sym(); + // // store into symbol table! + // SimpleAtom val; + // if (i0.get_src(0).is_reg(rs7())) { + // // store a false + // val = SimpleAtom::make_sym_val("#f"); + // } else if (i0.get_src(0).is_reg(rr0())) { + // // store a 0 + // val = SimpleAtom::make_int_constant(0); + // } else { + // // store a register. + // val = make_src_atom(i0.get_src(0).get_reg(), idx); + // } + // return std::make_unique(4, StoreOp::Kind::INTEGER, + // SimpleAtom::make_sym_val(name).as_expr(), val, idx); + } else { + return handle_generic_store(out, i0, instr_str); + } +} + +std::string dest_to_char(u8 x) { + if (x == 0) { + return "NONE"; + } + std::string dest; + if (x & 8) + dest.push_back('x'); + if (x & 4) + dest.push_back('y'); + if (x & 2) + dest.push_back('z'); + if (x & 1) + dest.push_back('w'); + return dest; +} + +Mips2C_Line handle_generic_op3_bc_mask(const Instruction& i0, + const std::string& instr_str, + const std::string& op_name) { + return {fmt::format("c->{}(DEST::{}, BC::{}, {}, {}, {});", op_name, dest_to_char(i0.cop2_dest), + i0.cop2_bc_to_char(), reg_to_name(i0.get_dst(0)), reg_to_name(i0.get_src(0)), + reg_to_name(i0.get_src(1))), + instr_str}; +} + +Mips2C_Line handle_generic_op3_mask(const Instruction& i0, + const std::string& instr_str, + const std::string& op_name) { + return {fmt::format("c->{}(DEST::{}, {}, {}, {});", op_name, dest_to_char(i0.cop2_dest), + reg_to_name(i0.get_dst(0)), reg_to_name(i0.get_src(0)), + reg_to_name(i0.get_src(1))), + instr_str}; +} + +Mips2C_Line handle_generic_op3(const Instruction& i0, + const std::string& instr_str, + const std::optional& name_override) { + return {fmt::format("c->{}({}, {}, {});", name_override ? *name_override : i0.op_name_to_string(), + reg_to_name(i0.get_dst(0)), reg_to_name(i0.get_src(0)), + reg_to_name(i0.get_src(1))), + instr_str}; +} + +Mips2C_Line handle_generic_op2_mask(const Instruction& i0, + const std::string& instr_str, + const std::string& op_name) { + return {fmt::format("c->{}(DEST::{}, {}, {});", op_name, dest_to_char(i0.cop2_dest), + reg_to_name(i0.get_dst(0)), reg_to_name(i0.get_src(0))), + instr_str}; +} + +Mips2C_Line handle_generic_op2(const Instruction& i0, + const std::string& instr_str, + const std::string& op_name) { + return {fmt::format("c->{}({}, {});", op_name, reg_to_name(i0.get_dst(0)), + reg_to_name(i0.get_src(0))), + instr_str}; +} + +Mips2C_Line handle_or(const Instruction& i0, const std::string& instr_str) { + if (is_gpr_3(i0, InstructionKind::OR, {}, rs7(), rr0())) { + // set reg_dest to #f : or reg_dest, s7, r0 + return handle_unknown(instr_str); + } else if (is_gpr_3(i0, InstructionKind::OR, {}, rr0(), rr0())) { + // set reg_dest to 0 : or reg_dest, r0, r0 + return handle_unknown(instr_str); + } else if (is_gpr_3(i0, InstructionKind::OR, {}, {}, rr0())) { + // set dst to src : or dst, src, r0 + return { + fmt::format("c->mov64({}, {});", reg_to_name(i0.get_dst(0)), reg_to_name(i0.get_src(0))), + instr_str}; + } else { + // actually do a logical OR of two registers: or a0, a1, a2 + return handle_unknown(instr_str); + } +} + +Mips2C_Line handle_sll(const Instruction& i0, const std::string& instr_str) { + if (is_nop(i0)) { + return {"// nop", instr_str}; + } else { + return handle_generic_op2_u16(i0, instr_str); + } +} + +Mips2C_Line handle_vmula_bc(const Instruction& i0, const std::string& instr_str) { + return {fmt::format("c->vmula_bc(DEST::{}, BC::{}, {}, {});", dest_to_char(i0.cop2_dest), + i0.cop2_bc_to_char(), reg_to_name(i0.get_src(0)), reg_to_name(i0.get_src(1))), + instr_str}; +} + +Mips2C_Line handle_vmadda_bc(const Instruction& i0, const std::string& instr_str) { + return {fmt::format("c->vmadda_bc(DEST::{}, BC::{}, {}, {});", dest_to_char(i0.cop2_dest), + i0.cop2_bc_to_char(), reg_to_name(i0.get_src(0)), reg_to_name(i0.get_src(1))), + instr_str}; +} + +std::string reg64_or_zero(const InstructionAtom& atom) { + if (atom.is_reg(Register(Reg::GPR, Reg::R0))) { + return "0"; + } else { + return fmt::format("c->sgpr64({})", atom.get_reg().to_string()); + } +} + +Mips2C_Line handle_branch_reg2(const Instruction& i0, + const std::string& instr_str, + const std::string& op_name) { + return {fmt::format("bc = {} {} {};", reg64_or_zero(i0.get_src(0)), op_name, + reg64_or_zero(i0.get_src(1))), + instr_str}; +} + +Mips2C_Line handle_non_likely_branch_bc(const Instruction& i0, const std::string& instr_str) { + switch (i0.kind) { + case InstructionKind::BNE: + return handle_branch_reg2(i0, instr_str, "!="); + case InstructionKind::BEQ: + return handle_branch_reg2(i0, instr_str, "=="); + case InstructionKind::BLTZ: + return {fmt::format("bc = ((s64){}) < 0;", reg64_or_zero(i0.get_src(0))), instr_str}; + case InstructionKind::BGTZ: + return {fmt::format("bc = ((s64){}) > 0;", reg64_or_zero(i0.get_src(0))), instr_str}; + default: + return handle_unknown(instr_str); + } +} + +Mips2C_Line handle_vdiv(const Instruction& i0, const std::string& instr_string) { + return {fmt::format("c->vdiv({}, BC::{}, {}, BC::{});", reg_to_name(i0.get_src(0)), + "xyzw"[i0.get_src(1).get_vf_field()], reg_to_name(i0.get_src(2)), + "xyzw"[i0.get_src(3).get_vf_field()]), + instr_string}; +} + +Mips2C_Line handle_por(const Instruction& i0, const std::string& instr_string) { + if (is_gpr_3(i0, InstructionKind::POR, {}, {}, rr0())) { + return {fmt::format("c->mov128_gpr_gpr({}, {});", reg_to_name(i0.get_dst(0)), + reg_to_name(i0.get_src(0))), + instr_string}; + } else { + return handle_generic_op3(i0, instr_string, {}); + } +} + +Mips2C_Line handle_normal_instr(Mips2C_Output& output, + const Instruction& i0, + const std::string& instr_str, + int& unknown_count) { + switch (i0.kind) { + case InstructionKind::LW: + return handle_lw(output, i0, instr_str); + case InstructionKind::LBU: + case InstructionKind::LWU: + case InstructionKind::LQ: + case InstructionKind::LQC2: + return handle_generic_load(i0, instr_str); + case InstructionKind::SQ: + case InstructionKind::SQC2: + return handle_generic_store(output, i0, instr_str); + case InstructionKind::VADD_BC: + return handle_generic_op3_bc_mask(i0, instr_str, "vadd_bc"); + case InstructionKind::VSUB_BC: + return handle_generic_op3_bc_mask(i0, instr_str, "vsub_bc"); + case InstructionKind::VMUL_BC: + return handle_generic_op3_bc_mask(i0, instr_str, "vmul_bc"); + case InstructionKind::VMUL: + return handle_generic_op3_mask(i0, instr_str, "vmul"); + case InstructionKind::VADD: + return handle_generic_op3_mask(i0, instr_str, "vadd"); + case InstructionKind::VSUB: + return handle_generic_op3_mask(i0, instr_str, "vsub"); + case InstructionKind::OR: + return handle_or(i0, instr_str); + case InstructionKind::SW: + return handle_sw(output, i0, instr_str); + case InstructionKind::VMOVE: + return handle_generic_op2_mask(i0, instr_str, "vmove"); + case InstructionKind::VITOF0: + return handle_generic_op2_mask(i0, instr_str, "vitof0"); + case InstructionKind::VFTOI4: + return handle_generic_op2_mask(i0, instr_str, "vftoi4"); + case InstructionKind::ANDI: + case InstructionKind::ORI: + case InstructionKind::SRA: + return handle_generic_op2_u16(i0, instr_str); + case InstructionKind::SLL: + return handle_sll(i0, instr_str); + case InstructionKind::DADDU: + case InstructionKind::ADDU: + case InstructionKind::PEXTLH: + case InstructionKind::PEXTLB: + case InstructionKind::MOVN: + case InstructionKind::MOVZ: + return handle_generic_op3(i0, instr_str, {}); + case InstructionKind::AND: + return handle_generic_op3(i0, instr_str, "and_"); // and isn't allowed in C++ + case InstructionKind::DADDIU: + case InstructionKind::ADDIU: + return handle_generic_op2_u16(i0, instr_str); + case InstructionKind::QMTC2: + return handle_generic_op2(i0, instr_str, "mov128_vf_gpr"); + case InstructionKind::QMFC2: + return handle_generic_op2(i0, instr_str, "mov128_gpr_vf"); + case InstructionKind::VMULA_BC: + return handle_vmula_bc(i0, instr_str); + case InstructionKind::VMADDA_BC: + return handle_vmadda_bc(i0, instr_str); + case InstructionKind::VMADD_BC: + return handle_generic_op3_bc_mask(i0, instr_str, "vmadd_bc"); + case InstructionKind::VDIV: + return handle_vdiv(i0, instr_str); + case InstructionKind::POR: + return handle_por(i0, instr_str); + case InstructionKind::VMULQ: + return handle_generic_op2_mask(i0, instr_str, "vmulq"); + case InstructionKind::VNOP: + return {"// nop", instr_str}; + case InstructionKind::MFC1: + return handle_generic_op2(i0, instr_str, "mfc1"); + default: + unknown_count++; + return handle_unknown(instr_str); + + break; + } +} + +void run_mips2c(Function* f) { + g_unknown = 0; + auto* file = f->ir2.env.file; + auto blocks = setup_preds_and_succs(*f, *file); + Mips2C_Output output; + int unknown_count = 0; + + for (size_t block_idx = 0; block_idx < blocks.size(); block_idx++) { + const auto& block = blocks[block_idx]; + // fmt::print("block {}: {} to {}\n", block_idx, block.start_instr, block.end_instr); + if (block_requires_label(f, blocks, block_idx)) { + output.output_label(block_idx); + } else { + // output.comment_line("block {}",) + } + + for (int i = block.start_instr; i < block.end_instr; i++) { + size_t old_line_count = output.lines.size(); + auto& instr = f->instructions.at(i); + auto instr_str = instr.to_string(file->labels); + + if (is_branch(instr, {})) { + if (block.branch_likely) { + output.lines.push_back(handle_unknown(instr_str)); + } else { + if (is_always_branch(instr)) { + // skip the branch ins. + output.lines.emplace_back("//" + instr_str, instr_str); + // then the delay slot + assert(i + 1 < block.end_instr); + i++; + auto& delay_i = f->instructions.at(i); + auto delay_i_str = delay_i.to_string(file->labels); + output.lines.push_back( + handle_normal_instr(output, delay_i, delay_i_str, unknown_count)); + assert(i + 1 == block.end_instr); + // then the goto + output.lines.emplace_back(fmt::format("goto block_{};", block.succ_branch), + "branch always\n"); + } else { + // set the branch condition + output.lines.push_back(handle_non_likely_branch_bc(instr, instr_str)); + // then the delay slot + assert(i + 1 < block.end_instr); + i++; + auto& delay_i = f->instructions.at(i); + auto delay_i_str = delay_i.to_string(file->labels); + output.lines.push_back( + handle_normal_instr(output, delay_i, delay_i_str, unknown_count)); + assert(i + 1 == block.end_instr); + // then the goto + output.lines.emplace_back(fmt::format("if (bc) {{goto block_{};}}", block.succ_branch), + "branch non-likely\n"); + } + } + } else if (is_jr_ra(instr)) { + // skip + output.lines.emplace_back("//" + instr_str, instr_str); + // then the delay slot + assert(i + 1 < block.end_instr); + i++; + auto& delay_i = f->instructions.at(i); + auto delay_i_str = delay_i.to_string(file->labels); + output.lines.push_back(handle_normal_instr(output, delay_i, delay_i_str, unknown_count)); + assert(i + 1 == block.end_instr); + // then the goto + output.lines.emplace_back(fmt::format("goto end_of_function;", block.succ_branch), + "return\n"); + } else { + output.lines.push_back(handle_normal_instr(output, instr, instr_str, unknown_count)); + } + // fmt::print("I: {}\n", instr_str); + + assert(output.lines.size() > old_line_count); + } + } + + f->mips2c_output = output.write_to_string(f->guessed_name); + if (g_unknown > 0) { + lg::error("Mips to C pass in {} hit {} unknown instructions", f->name(), g_unknown); + } +} +} // namespace decompiler diff --git a/decompiler/analysis/mips2c.h b/decompiler/analysis/mips2c.h new file mode 100644 index 0000000000..acf922ef20 --- /dev/null +++ b/decompiler/analysis/mips2c.h @@ -0,0 +1,7 @@ +#pragma once + +namespace decompiler { +class Function; + +void run_mips2c(Function* f); +} // namespace decompiler diff --git a/decompiler/analysis/reg_usage.h b/decompiler/analysis/reg_usage.h index a11b5435af..cea2378dad 100644 --- a/decompiler/analysis/reg_usage.h +++ b/decompiler/analysis/reg_usage.h @@ -1,7 +1,8 @@ #pragma once -#include #include +#include + #include "decompiler/Disasm/Register.h" namespace decompiler { diff --git a/decompiler/analysis/stack_spill.cpp b/decompiler/analysis/stack_spill.cpp index 49d14d08d2..b7589282f3 100644 --- a/decompiler/analysis/stack_spill.cpp +++ b/decompiler/analysis/stack_spill.cpp @@ -1,7 +1,8 @@ #include -#include "third-party/fmt/core.h" -#include "stack_spill.h" + #include "decompiler/Disasm/DecompilerLabel.h" +#include "stack_spill.h" +#include "third-party/fmt/core.h" namespace decompiler { diff --git a/decompiler/analysis/stack_spill.h b/decompiler/analysis/stack_spill.h index 451f48884a..8e8a71fa66 100644 --- a/decompiler/analysis/stack_spill.h +++ b/decompiler/analysis/stack_spill.h @@ -1,10 +1,11 @@ #pragma once +#include #include #include -#include -#include "decompiler/Disasm/Instruction.h" + #include "common/util/Range.h" +#include "decompiler/Disasm/Instruction.h" #include "decompiler/util/StackSpillMap.h" namespace decompiler { diff --git a/decompiler/analysis/symbol_def_map.cpp b/decompiler/analysis/symbol_def_map.cpp index 3b0a2227d6..62694a1b10 100644 --- a/decompiler/analysis/symbol_def_map.cpp +++ b/decompiler/analysis/symbol_def_map.cpp @@ -1,7 +1,7 @@ -#include "common/link_types.h" #include "symbol_def_map.h" -#include "third-party/json.hpp" +#include "common/link_types.h" #include "decompiler/ObjectFile/ObjectFileDB.h" +#include "third-party/json.hpp" namespace decompiler { @@ -92,7 +92,12 @@ std::optional get_loaded_or_stored_symbol_name(const AtomicOp* op) void SymbolMapBuilder::add_load_store_from_function(const Function& f, ObjectSymbolList* output) { if (!f.ir2.atomic_ops_succeeded) { - lg::error("Atomic ops failed in {}", f.guessed_name.to_string()); + if (!f.suspected_asm) { + // some asm functions will use mips2c which doesn't require atomic ops. + // we can't do anything with these, but we shouldn't warn. + lg::error("Atomic ops failed in {}", f.name()); + } + return; } diff --git a/decompiler/analysis/symbol_def_map.h b/decompiler/analysis/symbol_def_map.h index ad821d4b7e..b2805f77b2 100644 --- a/decompiler/analysis/symbol_def_map.h +++ b/decompiler/analysis/symbol_def_map.h @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include namespace decompiler { diff --git a/decompiler/analysis/type_analysis.cpp b/decompiler/analysis/type_analysis.cpp index a3491aae35..94304a5fc4 100644 --- a/decompiler/analysis/type_analysis.cpp +++ b/decompiler/analysis/type_analysis.cpp @@ -129,8 +129,7 @@ bool run_type_analysis_ir2(const TypeSpec& my_type, DecompilerTypeSystem& dts, F try { op_types.at(op_id) = op->propagate_types(*init_types, func.ir2.env, dts); } catch (std::runtime_error& e) { - lg::warn("Function {} failed type prop at op {}: {}", func.guessed_name.to_string(), - op_id, e.what()); + lg::warn("Function {} failed type prop at op {}: {}", func.name(), op_id, e.what()); func.warnings.type_prop_warning("Failed type prop at op {} ({}): {}", op_id, op->to_string(func.ir2.env), e.what()); func.ir2.env.set_types(block_init_types, op_types, *func.ir2.atomic_ops, my_type); diff --git a/decompiler/analysis/type_analysis.h b/decompiler/analysis/type_analysis.h index 7957f77fdb..1ce08a7756 100644 --- a/decompiler/analysis/type_analysis.h +++ b/decompiler/analysis/type_analysis.h @@ -1,12 +1,13 @@ #pragma once -#include -#include #include +#include +#include + #include "common/type_system/TypeSpec.h" +#include "decompiler/ObjectFile/LinkedObjectFile.h" #include "decompiler/config.h" #include "decompiler/util/DecompilerTypeSystem.h" -#include "decompiler/ObjectFile/LinkedObjectFile.h" namespace decompiler { bool run_type_analysis_ir2(const TypeSpec& my_type, DecompilerTypeSystem& dts, Function& func); diff --git a/decompiler/analysis/variable_naming.cpp b/decompiler/analysis/variable_naming.cpp index 4413445362..2991b1c6a8 100644 --- a/decompiler/analysis/variable_naming.cpp +++ b/decompiler/analysis/variable_naming.cpp @@ -1,10 +1,11 @@ #include -#include "variable_naming.h" -#include "reg_usage.h" + #include "decompiler/Function/Function.h" -#include "decompiler/util/DecompilerTypeSystem.h" #include "decompiler/IR2/Env.h" +#include "decompiler/util/DecompilerTypeSystem.h" +#include "reg_usage.h" #include "third-party/fmt/core.h" +#include "variable_naming.h" namespace decompiler { diff --git a/decompiler/analysis/variable_naming.h b/decompiler/analysis/variable_naming.h index 548c33b5af..654ab56fbd 100644 --- a/decompiler/analysis/variable_naming.h +++ b/decompiler/analysis/variable_naming.h @@ -15,14 +15,15 @@ #pragma once -#include -#include #include +#include #include +#include + #include "common/util/assert.h" #include "decompiler/Disasm/Register.h" -#include "decompiler/util/TP_Type.h" #include "decompiler/IR2/IR2_common.h" +#include "decompiler/util/TP_Type.h" namespace decompiler { diff --git a/decompiler/config.cpp b/decompiler/config.cpp index 60c88525bc..352c0ac376 100644 --- a/decompiler/config.cpp +++ b/decompiler/config.cpp @@ -176,6 +176,8 @@ Config read_config_file(const std::string& path_to_config_file) { config.hacks.format_ops_with_dynamic_string_by_func_name = hacks_json.at("dynamic_format_arg_counts") .get>>>(); + config.hacks.mips2c_functions_by_name = + hacks_json.at("mips2c_functions_by_name").get>(); for (auto& entry : hacks_json.at("cond_with_else_max_lengths")) { auto func_name = entry.at(0).get(); diff --git a/decompiler/config.h b/decompiler/config.h index 87c07e123a..5994a02ca2 100644 --- a/decompiler/config.h +++ b/decompiler/config.h @@ -75,6 +75,7 @@ struct DecompileHacks { std::unordered_map> blocks_ending_in_asm_branch_by_func_name; std::unordered_map>> format_ops_with_dynamic_string_by_func_name; + std::unordered_set mips2c_functions_by_name; }; struct Config { diff --git a/decompiler/config/jak1_ntsc_black_label/hacks.jsonc b/decompiler/config/jak1_ntsc_black_label/hacks.jsonc index 721b9be616..46de186ff4 100644 --- a/decompiler/config/jak1_ntsc_black_label/hacks.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/hacks.jsonc @@ -539,5 +539,11 @@ [94, 1] ], "":[] - } + }, + + "mips2c_functions_by_name":[ + "draw-string" + ] } + + diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 222e9de53b..0eee1117cf 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -58,6 +58,9 @@ set(RUNTIME_SOURCE kernel/kscheme.cpp kernel/ksocket.cpp kernel/ksound.cpp + mips2c/mips2c_table.cpp + mips2c/functions/draw_string.cpp + mips2c/functions/test_func.cpp overlord/dma.cpp overlord/fake_iso.cpp overlord/iso.cpp diff --git a/game/kernel/Ptr.h b/game/kernel/Ptr.h index 69ec0b7d4f..4bbb19793f 100644 --- a/game/kernel/Ptr.h +++ b/game/kernel/Ptr.h @@ -5,9 +5,6 @@ * Representation of a GOAL pointer which can be converted to/from a C pointer. */ -#ifndef JAK_PTR_H -#define JAK_PTR_H - #include "common/util/assert.h" #include "game/runtime.h" #include "common/common_types.h" @@ -88,5 +85,3 @@ Ptr make_u8_ptr(T* x) { } return Ptr((u8*)x - g_ee_main_mem); } - -#endif // JAK_PTR_H diff --git a/game/kernel/asm_funcs.asm b/game/kernel/asm_funcs.asm index 21cd984c74..543e889351 100644 --- a/game/kernel/asm_funcs.asm +++ b/game/kernel/asm_funcs.asm @@ -102,6 +102,147 @@ _stack_call_linux: ; return! ret +;; Call c++ code through mips2c. +;; GOAL will call a dynamically generated trampoline. +;; The trampoline will have pushed the exec function and stack offset onto the stack +global _mips2c_call_linux +_mips2c_call_linux: + ;; grab the address to call and put it in xmm0 + sub rsp, 8 + movaps xmm0, [rsp + 16] + ;; grab the stack offset + mov rax, [rsp + 8] + + ;; first, save xmms + sub rsp, 128 + movaps [rsp], xmm8 + movaps [rsp + 16], xmm9 + movaps [rsp + 32], xmm10 + movaps [rsp + 48], xmm11 + movaps [rsp + 64], xmm12 + movaps [rsp + 80], xmm13 + movaps [rsp + 96], xmm14 + movaps [rsp + 112], xmm15 + + push r10 + push r11 + + ;; oof + sub rsp, 1280 + mov [rsp + 64], rdi ;; arg0 + mov [rsp + 80], rsi ;; arg1 + mov [rsp + 96], rdx ;; arg2 + mov [rsp + 112], rcx ;; arg3 + mov [rsp + 128], r8 ;; arg4 + mov [rsp + 144], r9 ;; arg5 + mov [rsp + 160], r10 ;; arg6 + mov [rsp + 176], r11 ;; arg7 + mov [rsp + 464], rsp ;; mip2c code's MIPS stack + + mov rdi, rsp + + + sub rsp, rax ;; allocate space on the stack for GOAL fake stack + push rax ;; and remember this so we can find our way back + push rax + + movq rax, xmm0 + + call rax ;; call! + + ;; unallocate + pop rax + pop rax + add rsp, rax + + mov rax, [rsp + 32] + + add rsp, 1280 + pop r11 + pop r10 + + movaps xmm8, [rsp] + movaps xmm9, [rsp + 16] + movaps xmm10, [rsp + 32] + movaps xmm11, [rsp + 48] + movaps xmm12, [rsp + 64] + movaps xmm13, [rsp + 80] + movaps xmm14, [rsp + 96] + movaps xmm15, [rsp + 112] + add rsp, 152 ;; 128 for xmm's + 16 for the stuff pushed by trampoline + 8 for stack alignment undo + + ret + +global _mips2c_call_windows +_mips2c_call_windows: + ;; grab the address to call and put it in xmm0 + sub rsp, 8 + movaps xmm0, [rsp + 16] + ;; grab the stack offset + mov rax, [rsp + 8] + + ;; first, save xmms + sub rsp, 128 + movaps [rsp], xmm8 + movaps [rsp + 16], xmm9 + movaps [rsp + 32], xmm10 + movaps [rsp + 48], xmm11 + movaps [rsp + 64], xmm12 + movaps [rsp + 80], xmm13 + movaps [rsp + 96], xmm14 + movaps [rsp + 112], xmm15 + + push r10 + push r11 + + ;; oof + sub rsp, 1280 + mov [rsp + 64], rdi ;; arg0 + mov [rsp + 80], rsi ;; arg1 + mov [rsp + 96], rdx ;; arg2 + mov [rsp + 112], rcx ;; arg3 + mov [rsp + 128], r8 ;; arg4 + mov [rsp + 144], r9 ;; arg5 + mov [rsp + 160], r10 ;; arg6 + mov [rsp + 176], r11 ;; arg7 + mov [rsp + 464], rsp ;; mip2c code's MIPS stack + + mov rcx, rsp + + sub rsp, rax ;; allocate space on the stack for GOAL fake stack + push rax ;; and remember this so we can find our way back + push rax + + movq rax, xmm0 + + sub rsp, 32 + call rax ;; call! + add rsp, 32 + + ;; unallocate + pop rax + pop rax + add rsp, rax + + mov rax, [rsp + 32] + + add rsp, 1280 + pop r11 + pop r10 + + movaps xmm8, [rsp] + movaps xmm9, [rsp + 16] + movaps xmm10, [rsp + 32] + movaps xmm11, [rsp + 48] + movaps xmm12, [rsp + 64] + movaps xmm13, [rsp + 80] + movaps xmm14, [rsp + 96] + movaps xmm15, [rsp + 112] + add rsp, 152 ;; 128 for xmm's + 16 for the stuff pushed by trampoline + 8 for stack alignment undo + + ret + + ;; Call C++ code on windows, from GOAL. Pug arguments on the stack and put a pointer to this array in the first arg. global _stack_call_win32 _stack_call_win32: diff --git a/game/kernel/klink.cpp b/game/kernel/klink.cpp index bdba8968e4..464f9dfa7c 100644 --- a/game/kernel/klink.cpp +++ b/game/kernel/klink.cpp @@ -17,6 +17,7 @@ #include "kprint.h" #include "common/symbols.h" #include "common/goal_constants.h" +#include "game/mips2c/mips2c_table.h" namespace { // turn on printf's for debugging linking issues. @@ -785,6 +786,11 @@ void link_control::finish() { if (ofh->object_file_version == 3) { // todo check function type of entry + // setup mips2c functions + for (auto& x : Mips2C::gMips2CLinkCallbacks[m_object_name]) { + x(); + } + // execute top level! if (m_entry.offset && (m_flags & LINK_FLAG_EXECUTE)) { call_goal(m_entry.cast(), 0, 0, 0, s7.offset, g_ee_main_mem); diff --git a/game/kernel/kmachine.cpp b/game/kernel/kmachine.cpp index 84f459bbf8..73843caade 100644 --- a/game/kernel/kmachine.cpp +++ b/game/kernel/kmachine.cpp @@ -34,7 +34,7 @@ #include "game/graphics/gfx.h" #include "game/graphics/dma/dma_chain_read.h" #include "game/graphics/dma/dma_copy.h" - +#include "game/mips2c/mips2c_table.h" #include "game/system/vm/vm.h" #include "game/system/newpad.h" using namespace ee; @@ -555,6 +555,12 @@ void pc_texture_relocate(u32 dst, u32 src, u32 format) { Gfx::texture_relocate(dst, src, format); } +u64 pc_get_mips2c(u32 name) { + const char* n = Ptr(name).c()->data(); + fmt::print("Getting mips: {}\n", n); + return Mips2C::gLinkedFunctionTable.get(n); +} + /*! * Open a file-stream. Name is a GOAL string. Mode is a GOAL symbol. Use 'read for readonly * and anything else for write only. @@ -718,6 +724,7 @@ void InitMachine_PCPort() { make_function_symbol_from_c("__send-gfx-dma-chain", (void*)send_gfx_dma_chain); make_function_symbol_from_c("__pc-texture-upload-now", (void*)pc_texture_upload_now); make_function_symbol_from_c("__pc-texture-relocate", (void*)pc_texture_relocate); + make_function_symbol_from_c("__pc-get-mips2c", (void*)pc_get_mips2c); // pad stuff make_function_symbol_from_c("pc-pad-get-mapped-button", (void*)Gfx::get_mapped_button); diff --git a/game/kernel/kscheme.h b/game/kernel/kscheme.h index 63bb074cf1..c73ab8d8cc 100644 --- a/game/kernel/kscheme.h +++ b/game/kernel/kscheme.h @@ -78,6 +78,7 @@ u32 crc32(const u8* data, s32 size); void kscheme_init_globals(); void init_crc(); u64 alloc_from_heap(u32 heapSymbol, u32 type, s32 size, u32 pp); +u64 alloc_heap_object(u32 heap, u32 type, u32 size, u32 pp); Ptr intern_from_c(const char* name); Ptr intern_type_from_c(const char* name, u64 methods); Ptr set_type_values(Ptr type, Ptr parent, u64 flags); diff --git a/game/mips2c/functions/draw_string.cpp b/game/mips2c/functions/draw_string.cpp new file mode 100644 index 0000000000..ee082a87d9 --- /dev/null +++ b/game/mips2c/functions/draw_string.cpp @@ -0,0 +1,1622 @@ +//--------------------------MIPS2C--------------------- +// clang-format off +#include "game/mips2c/mips2c_private.h" +#include "game/kernel/kscheme.h" +namespace Mips2C { +namespace draw_string { +struct Cache { + void* font_work; // *font-work* + void* font12_table; // *font12-table* + void* font24_table; // *font24-table* + void* math_camera; // *math-camera* + void* video_parms; // *video-parms* +} cache; + +u64 execute(void* ctxt) { + auto* c = (ExecutionContext*)ctxt; + bool bc = false; c->load_symbol(v1, cache.math_camera); // lw v1, *math-camera*(s7) + c->lqc2(vf26, 732, v1); // lqc2 vf26, 732(v1) + c->lqc2(vf27, 732, v1); // lqc2 vf27, 732(v1) + c->vadd_bc(DEST::xy, BC::w, vf26, vf26, vf0); // vaddw.xy vf26, vf26, vf0 + c->vadd_bc(DEST::x, BC::w, vf26, vf26, vf0); // vaddw.x vf26, vf26, vf0 + c->lw(v1, 72, a2); // lw v1, 72(a2) + c->lqc2(vf25, 44, a2); // lqc2 vf25, 44(a2) + c->lqc2(vf23, 12, a2); // lqc2 vf23, 12(a2) + c->lqc2(vf24, 12, a2); // lqc2 vf24, 12(a2) + c->lqc2(vf28, 0, v1); // lqc2 vf28, 0(v1) + c->lqc2(vf29, 16, v1); // lqc2 vf29, 16(v1) + c->lqc2(vf30, 32, v1); // lqc2 vf30, 32(v1) + c->lqc2(vf31, 48, v1); // lqc2 vf31, 48(v1) + c->load_symbol(v1, cache.video_parms); // lw v1, *video-parms*(s7) + c->mov64(v1, v1); // or v1, v1, r0 + c->lqc2(vf1, 64, v1); // lqc2 vf1, 64(v1) + c->vmul(DEST::xy, vf25, vf25, vf1); // vmul.xy vf25, vf25, vf1 + c->vmul(DEST::xy, vf23, vf23, vf1); // vmul.xy vf23, vf23, vf1 + c->vmul(DEST::xy, vf24, vf24, vf1); // vmul.xy vf24, vf24, vf1 + c->load_symbol(v1, cache.font_work); // lw v1, *font-work*(s7) + c->mov64(v1, v1); // or v1, v1, r0 + c->sw(a1, 3024, v1); // sw a1, 3024(v1) + c->lw(a1, 4, a1); // lw a1, 4(a1) + c->sw(a0, 3028, v1); // sw a0, 3028(v1) + c->lw(t2, 68, a2); // lw t2, 68(a2) + c->vmove(DEST::xyzw, vf1, vf0); // vmove.xyzw vf1, vf0 + c->vmove(DEST::xyzw, vf2, vf0); // vmove.xyzw vf2, vf0 + c->vmove(DEST::xyzw, vf3, vf0); // vmove.xyzw vf3, vf0 + c->vmove(DEST::xyzw, vf4, vf0); // vmove.xyzw vf4, vf0 + c->sw(t2, 3032, v1); // sw t2, 3032(v1) + c->lqc2(vf16, 240, v1); // lqc2 vf16, 240(v1) + c->lqc2(vf17, 256, v1); // lqc2 vf17, 256(v1) + c->lqc2(vf18, 272, v1); // lqc2 vf18, 272(v1) + c->andi(a3, t2, 32); // andi a3, t2, 32 + // nop // sll r0, r0, 0 + bc = c->sgpr64(a3) != 0; // bne a3, r0, L22 + c->load_symbol(a3, cache.font12_table); // lw a3, *font12-table*(s7) + if (bc) {goto block_2;} // branch non-likely + + c->mov64(a3, a3); // or a3, a3, r0 + // nop // sll r0, r0, 0 + c->lqc2(vf13, 144, v1); // lqc2 vf13, 144(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf14, 160, v1); // lqc2 vf14, 160(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf15, 176, v1); // lqc2 vf15, 176(v1) + // nop // sll r0, r0, 0 + c->lq(t0, 80, v1); // lq t0, 80(v1) + //beq r0, r0, L23 // beq r0, r0, L23 + c->lq(t1, 96, v1); // lq t1, 96(v1) + goto block_3; // branch always + + + block_2: + // nop // sll r0, r0, 0 + c->load_symbol(a3, cache.font24_table); // lw a3, *font24-table*(s7) + c->mov64(a3, a3); // or a3, a3, r0 + // nop // sll r0, r0, 0 + c->lqc2(vf13, 192, v1); // lqc2 vf13, 192(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf14, 208, v1); // lqc2 vf14, 208(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf15, 224, v1); // lqc2 vf15, 224(v1) + // nop // sll r0, r0, 0 + c->lq(t0, 112, v1); // lq t0, 112(v1) + // nop // sll r0, r0, 0 + c->lq(t1, 128, v1); // lq t1, 128(v1) + + block_3: + c->lw(t3, 60, a2); // lw t3, 60(a2) + // nop // sll r0, r0, 0 + c->sw(t3, 3008, v1); // sw t3, 3008(v1) + // nop // sll r0, r0, 0 + c->sll(t3, t3, 4); // sll t3, t3, 4 + // nop // sll r0, r0, 0 + c->daddu(t4, t3, v1); // daddu t4, t3, v1 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + c->lwu(t3, 1984, t4); // lwu t3, 1984(t4) + // nop // sll r0, r0, 0 + c->lwu(t5, 1988, t4); // lwu t5, 1988(t4) + c->pextlb(t6, r0, t3); // pextlb t6, r0, t3 + c->lwu(t3, 1992, t4); // lwu t3, 1992(t4) + c->pextlh(t6, r0, t6); // pextlh t6, r0, t6 + c->lwu(t4, 1996, t4); // lwu t4, 1996(t4) + c->pextlb(t5, r0, t5); // pextlb t5, r0, t5 + c->sq(t6, 432, v1); // sq t6, 432(v1) + c->pextlh(t5, r0, t5); // pextlh t5, r0, t5 + c->sq(t6, 816, v1); // sq t6, 816(v1) + c->pextlb(t3, r0, t3); // pextlb t3, r0, t3 + c->sq(t5, 448, v1); // sq t5, 448(v1) + c->pextlh(t3, r0, t3); // pextlh t3, r0, t3 + c->sq(t5, 832, v1); // sq t5, 832(v1) + c->pextlb(t4, r0, t4); // pextlb t4, r0, t4 + c->sq(t3, 464, v1); // sq t3, 464(v1) + c->pextlh(t4, r0, t4); // pextlh t4, r0, t4 + c->sq(t3, 848, v1); // sq t3, 848(v1) + // nop // sll r0, r0, 0 + c->sq(t4, 480, v1); // sq t4, 480(v1) + // nop // sll r0, r0, 0 + c->sq(t4, 864, v1); // sq t4, 864(v1) + c->mov64(t3, v1); // or t3, v1, r0 + // nop // sll r0, r0, 0 + + block_4: + c->lbu(t4, 4, a0); // lbu t4, 4(a0) + c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L47 + c->daddiu(t5, t4, -1); // daddiu t5, t4, -1 + if (bc) {goto block_67;} // branch non-likely + + bc = c->sgpr64(t5) == 0; // beq t5, r0, L39 + c->daddiu(t5, t4, -126); // daddiu t5, t4, -126 + if (bc) {goto block_54;} // branch non-likely + + bc = c->sgpr64(t5) != 0; // bne t5, r0, L40 + // nop // sll r0, r0, 0 + if (bc) {goto block_55;} // branch non-likely + + c->lbu(t4, 4, a0); // lbu t4, 4(a0) + c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 + c->addiu(t5, r0, 0); // addiu t5, r0, 0 + c->addiu(t6, r0, 0); // addiu t6, r0, 0 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L47 + c->daddiu(t7, t4, -43); // daddiu t7, t4, -43 + if (bc) {goto block_67;} // branch non-likely + + c->movz(t5, t4, t7); // movz t5, t4, t7 + c->daddiu(t7, t4, -45); // daddiu t7, t4, -45 + c->movz(t5, t4, t7); // movz t5, t4, t7 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t5) != 0; // bne t5, r0, L25 + c->daddiu(t7, t4, -121); // daddiu t7, t4, -121 + if (bc) {goto block_15;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L37 + c->daddiu(t6, t4, -89); // daddiu t6, t4, -89 + if (bc) {goto block_52;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L37 + c->daddiu(t6, t4, -122); // daddiu t6, t4, -122 + if (bc) {goto block_52;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L38 + c->daddiu(t6, t4, -90); // daddiu t6, t4, -90 + if (bc) {goto block_53;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L38 + c->daddiu(t6, t4, -48); // daddiu t6, t4, -48 + if (bc) {goto block_53;} // branch non-likely + + bc = ((s64)c->sgpr64(t6)) < 0; // bltz t6, L40 + c->daddiu(t6, t4, -57); // daddiu t6, t4, -57 + if (bc) {goto block_55;} // branch non-likely + + bc = ((s64)c->sgpr64(t6)) > 0; // bgtz t6, L40 + c->daddiu(t6, t4, -48); // daddiu t6, t4, -48 + if (bc) {goto block_55;} // branch non-likely + + + block_15: + c->lbu(t4, 4, a0); // lbu t4, 4(a0) + c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L47 + c->daddiu(t7, t4, -110); // daddiu t7, t4, -110 + if (bc) {goto block_67;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L26 + c->daddiu(t7, t4, -78); // daddiu t7, t4, -78 + if (bc) {goto block_33;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L26 + c->daddiu(t7, t4, -108); // daddiu t7, t4, -108 + if (bc) {goto block_33;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L24 + c->daddiu(t7, t4, -76); // daddiu t7, t4, -76 + if (bc) {goto block_4;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L24 + c->daddiu(t7, t4, -119); // daddiu t7, t4, -119 + if (bc) {goto block_4;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L24 + c->daddiu(t7, t4, -87); // daddiu t7, t4, -87 + if (bc) {goto block_4;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L24 + c->daddiu(t7, t4, -107); // daddiu t7, t4, -107 + if (bc) {goto block_4;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L28 + c->daddiu(t7, t4, -75); // daddiu t7, t4, -75 + if (bc) {goto block_36;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L28 + c->daddiu(t7, t4, -106); // daddiu t7, t4, -106 + if (bc) {goto block_36;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L29 + c->daddiu(t7, t4, -74); // daddiu t7, t4, -74 + if (bc) {goto block_38;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L29 + c->daddiu(t7, t4, -104); // daddiu t7, t4, -104 + if (bc) {goto block_38;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L31 + c->daddiu(t7, t4, -72); // daddiu t7, t4, -72 + if (bc) {goto block_42;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L31 + c->daddiu(t7, t4, -118); // daddiu t7, t4, -118 + if (bc) {goto block_42;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L34 + c->daddiu(t7, t4, -86); // daddiu t7, t4, -86 + if (bc) {goto block_47;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L34 + c->daddiu(t7, t4, -48); // daddiu t7, t4, -48 + if (bc) {goto block_47;} // branch non-likely + + bc = ((s64)c->sgpr64(t7)) < 0; // bltz t7, L40 + c->daddiu(t8, t4, -57); // daddiu t8, t4, -57 + if (bc) {goto block_55;} // branch non-likely + + bc = ((s64)c->sgpr64(t8)) > 0; // bgtz t8, L40 + c->sll(t8, t6, 2); // sll t8, t6, 2 + if (bc) {goto block_55;} // branch non-likely + + c->daddu(t4, t6, t8); // daddu t4, t6, t8 + // nop // sll r0, r0, 0 + c->sll(t4, t4, 1); // sll t4, t4, 1 + // nop // sll r0, r0, 0 + //beq r0, r0, L25 // beq r0, r0, L25 + c->daddu(t6, t4, t7); // daddu t6, t4, t7 + goto block_15; // branch always + + + block_33: + bc = c->sgpr64(t6) != 0; // bne t6, r0, L27 + c->addiu(t0, r0, -33); // addiu t0, r0, -33 + if (bc) {goto block_35;} // branch non-likely + + c->load_symbol(a3, cache.font12_table); // lw a3, *font12-table*(s7) + c->mov64(a3, a3); // or a3, a3, r0 + c->lqc2(vf13, 144, v1); // lqc2 vf13, 144(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf14, 160, v1); // lqc2 vf14, 160(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf15, 176, v1); // lqc2 vf15, 176(v1) + c->and_(t2, t2, t0); // and t2, t2, t0 + c->lq(t0, 80, v1); // lq t0, 80(v1) + //beq r0, r0, L24 // beq r0, r0, L24 + c->lq(t1, 96, v1); // lq t1, 96(v1) + goto block_4; // branch always + + + block_35: + // nop // sll r0, r0, 0 + c->load_symbol(a3, cache.font24_table); // lw a3, *font24-table*(s7) + c->mov64(a3, a3); // or a3, a3, r0 + // nop // sll r0, r0, 0 + c->lqc2(vf13, 192, v1); // lqc2 vf13, 192(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf14, 208, v1); // lqc2 vf14, 208(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf15, 224, v1); // lqc2 vf15, 224(v1) + c->ori(t2, t2, 32); // ori t2, t2, 32 + c->lq(t0, 112, v1); // lq t0, 112(v1) + //beq r0, r0, L24 // beq r0, r0, L24 + c->lq(t1, 128, v1); // lq t1, 128(v1) + goto block_4; // branch always + + + block_36: + c->addiu(t4, r0, -3); // addiu t4, r0, -3 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t6) == 0; // beq t6, r0, L24 + c->and_(t2, t2, t4); // and t2, t2, t4 + if (bc) {goto block_4;} // branch non-likely + + //beq r0, r0, L24 // beq r0, r0, L24 + c->ori(t2, t2, 2); // ori t2, t2, 2 + goto block_4; // branch always + + + block_38: + c->addiu(t4, r0, -21); // addiu t4, r0, -21 + c->daddiu(t5, t6, -2); // daddiu t5, t6, -2 + bc = c->sgpr64(t6) == 0; // beq t6, r0, L24 + c->and_(t2, t2, t4); // and t2, t2, t4 + if (bc) {goto block_4;} // branch non-likely + + bc = c->sgpr64(t5) == 0; // beq t5, r0, L30 + // nop // sll r0, r0, 0 + if (bc) {goto block_41;} // branch non-likely + + //beq r0, r0, L24 // beq r0, r0, L24 + c->ori(t2, t2, 16); // ori t2, t2, 16 + goto block_4; // branch always + + + block_41: + //beq r0, r0, L24 // beq r0, r0, L24 + c->ori(t2, t2, 4); // ori t2, t2, 4 + goto block_4; // branch always + + + block_42: + c->mov128_vf_gpr(vf1, t6); // qmtc2.i vf1, t6 + c->daddiu(t4, t5, -45); // daddiu t4, t5, -45 + bc = c->sgpr64(t5) == 0; // beq t5, r0, L33 + c->vitof0(DEST::xyzw, vf1, vf1); // vitof0.xyzw vf1, vf1 + if (bc) {goto block_46;} // branch non-likely + + bc = c->sgpr64(t4) == 0; // beq t4, r0, L32 + // nop // sll r0, r0, 0 + if (bc) {goto block_45;} // branch non-likely + + //beq r0, r0, L24 // beq r0, r0, L24 + c->vadd_bc(DEST::x, BC::x, vf23, vf23, vf1); // vaddx.x vf23, vf23, vf1 + goto block_4; // branch always + + + block_45: + //beq r0, r0, L24 // beq r0, r0, L24 + c->vsub_bc(DEST::x, BC::x, vf23, vf23, vf1); // vsubx.x vf23, vf23, vf1 + goto block_4; // branch always + + + block_46: + //beq r0, r0, L24 // beq r0, r0, L24 + c->vadd_bc(DEST::x, BC::x, vf23, vf0, vf1); // vaddx.x vf23, vf0, vf1 + goto block_4; // branch always + + + block_47: + c->mov128_vf_gpr(vf1, t6); // qmtc2.i vf1, t6 + c->daddiu(t4, t5, -45); // daddiu t4, t5, -45 + bc = c->sgpr64(t5) == 0; // beq t5, r0, L36 + c->vitof0(DEST::xyzw, vf1, vf1); // vitof0.xyzw vf1, vf1 + if (bc) {goto block_51;} // branch non-likely + + bc = c->sgpr64(t4) == 0; // beq t4, r0, L35 + // nop // sll r0, r0, 0 + if (bc) {goto block_50;} // branch non-likely + + //beq r0, r0, L24 // beq r0, r0, L24 + c->vadd_bc(DEST::y, BC::x, vf23, vf23, vf1); // vaddx.y vf23, vf23, vf1 + goto block_4; // branch always + + + block_50: + //beq r0, r0, L24 // beq r0, r0, L24 + c->vsub_bc(DEST::y, BC::x, vf23, vf23, vf1); // vsubx.y vf23, vf23, vf1 + goto block_4; // branch always + + + block_51: + //beq r0, r0, L24 // beq r0, r0, L24 + c->vadd_bc(DEST::y, BC::x, vf23, vf0, vf1); // vaddx.y vf23, vf0, vf1 + goto block_4; // branch always + + + block_52: + //beq r0, r0, L24 // beq r0, r0, L24 + c->sqc2(vf23, 288, v1); // sqc2 vf23, 288(v1) + goto block_4; // branch always + + + block_53: + //beq r0, r0, L24 // beq r0, r0, L24 + c->lqc2(vf23, 288, v1); // lqc2 vf23, 288(v1) + goto block_4; // branch always + + + block_54: + c->lbu(t4, 4, a0); // lbu t4, 4(a0) + c->daddiu(a0, a0, 1); // daddiu a0, a0, 1 + c->vadd(DEST::xyz, vf4, vf23, vf15); // vadd.xyz vf4, vf23, vf15 + c->andi(t4, t4, 127); // andi t4, t4, 127 + c->vsub(DEST::xyzw, vf1, vf25, vf23); // vsub.xyzw vf1, vf25, vf23 + c->daddiu(t4, t4, 255); // daddiu t4, t4, 255 + //beq r0, r0, L44 // beq r0, r0, L44 + c->sll(t5, t4, 4); // sll t5, t4, 4 + goto block_62; // branch always + + + block_55: + c->vadd(DEST::xyz, vf4, vf23, vf15); // vadd.xyz vf4, vf23, vf15 + c->sll(t5, t4, 4); // sll t5, t4, 4 + c->vsub(DEST::xyzw, vf1, vf25, vf23); // vsub.xyzw vf1, vf25, vf23 + c->daddiu(t6, t4, -10); // daddiu t6, t4, -10 + bc = c->sgpr64(t6) == 0; // beq t6, r0, L41 + c->daddiu(t4, t4, -13); // daddiu t4, t4, -13 + if (bc) {goto block_57;} // branch non-likely + + bc = c->sgpr64(t4) != 0; // bne t4, r0, L44 + // nop // sll r0, r0, 0 + if (bc) {goto block_62;} // branch non-likely + + + block_57: + c->vsub(DEST::xyzw, vf1, vf23, vf24); // vsub.xyzw vf1, vf23, vf24 + c->andi(t4, t2, 16); // andi t4, t2, 16 + bc = c->sgpr64(t4) != 0; // bne t4, r0, L42 + c->andi(t4, t2, 4); // andi t4, t2, 4 + if (bc) {goto block_60;} // branch non-likely + + bc = c->sgpr64(t4) != 0; // bne t4, r0, L43 + // nop // sll r0, r0, 0 + if (bc) {goto block_61;} // branch non-likely + + c->vadd_bc(DEST::x, BC::x, vf23, vf0, vf24); // vaddx.x vf23, vf0, vf24 + // nop // sll r0, r0, 0 + c->sqc2(vf23, 944, t3); // sqc2 vf23, 944(t3) + c->vadd_bc(DEST::y, BC::w, vf23, vf23, vf15); // vaddw.y vf23, vf23, vf15 + //beq r0, r0, L24 // beq r0, r0, L24 + c->daddiu(t3, t3, 16); // daddiu t3, t3, 16 + goto block_4; // branch always + + + block_60: + c->vsub(DEST::x, vf23, vf24, vf1); // vsub.x vf23, vf24, vf1 + c->sqc2(vf23, 944, t3); // sqc2 vf23, 944(t3) + c->vadd_bc(DEST::x, BC::x, vf23, vf0, vf24); // vaddx.x vf23, vf0, vf24 + c->vadd_bc(DEST::y, BC::w, vf23, vf23, vf15); // vaddw.y vf23, vf23, vf15 + //beq r0, r0, L24 // beq r0, r0, L24 + c->daddiu(t3, t3, 16); // daddiu t3, t3, 16 + goto block_4; // branch always + + + block_61: + c->vmul_bc(DEST::x, BC::w, vf1, vf1, vf16); // vmulw.x vf1, vf1, vf16 + // nop // sll r0, r0, 0 + c->vsub(DEST::x, vf23, vf24, vf1); // vsub.x vf23, vf24, vf1 + c->sqc2(vf23, 944, t3); // sqc2 vf23, 944(t3) + c->vadd_bc(DEST::x, BC::x, vf23, vf0, vf24); // vaddx.x vf23, vf0, vf24 + c->vadd_bc(DEST::y, BC::w, vf23, vf23, vf15); // vaddw.y vf23, vf23, vf15 + //beq r0, r0, L24 // beq r0, r0, L24 + c->daddiu(t3, t3, 16); // daddiu t3, t3, 16 + goto block_4; // branch always + + + block_62: + c->addu(t4, t5, a3); // addu t4, t5, a3 + // nop // sll r0, r0, 0 + c->lqc2(vf5, -256, t4); // lqc2 vf5, -256(t4) + c->mov128_gpr_vf(t4, vf1); // qmfc2.i t4, vf1 + bc = ((s64)c->sgpr64(t4)) < 0; // bltz t4, L47 + c->sra(t4, t4, 31); // sra t4, t4, 31 + if (bc) {goto block_67;} // branch non-likely + + c->vmul(DEST::xyzw, vf19, vf5, vf13); // vmul.xyzw vf19, vf5, vf13 + c->andi(t4, t2, 2); // andi t4, t2, 2 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L45 + // nop // sll r0, r0, 0 + if (bc) {goto block_65;} // branch non-likely + + //beq r0, r0, L46 // beq r0, r0, L46 + c->vadd_bc(DEST::x, BC::w, vf23, vf23, vf19); // vaddw.x vf23, vf23, vf19 + goto block_66; // branch always + + + block_65: + // nop // sll r0, r0, 0 + c->vadd_bc(DEST::x, BC::w, vf23, vf23, vf14); // vaddw.x vf23, vf23, vf14 + + block_66: + //beq r0, r0, L24 // beq r0, r0, L24 + // nop // sll r0, r0, 0 + goto block_4; // branch always + + + block_67: + c->vsub(DEST::xyzw, vf1, vf23, vf24); // vsub.xyzw vf1, vf23, vf24 + c->andi(a0, t2, 16); // andi a0, t2, 16 + bc = c->sgpr64(a0) != 0; // bne a0, r0, L48 + c->andi(a0, t2, 4); // andi a0, t2, 4 + if (bc) {goto block_70;} // branch non-likely + + bc = c->sgpr64(a0) != 0; // bne a0, r0, L49 + // nop // sll r0, r0, 0 + if (bc) {goto block_71;} // branch non-likely + + c->vadd_bc(DEST::x, BC::x, vf23, vf0, vf24); // vaddx.x vf23, vf0, vf24 + // nop // sll r0, r0, 0 + //beq r0, r0, L50 // beq r0, r0, L50 + c->sqc2(vf23, 944, t3); // sqc2 vf23, 944(t3) + goto block_72; // branch always + + + block_70: + c->vsub(DEST::x, vf23, vf24, vf1); // vsub.x vf23, vf24, vf1 + // nop // sll r0, r0, 0 + //beq r0, r0, L50 // beq r0, r0, L50 + c->sqc2(vf23, 944, t3); // sqc2 vf23, 944(t3) + goto block_72; // branch always + + + block_71: + c->vmul_bc(DEST::x, BC::w, vf1, vf1, vf16); // vmulw.x vf1, vf1, vf16 + // nop // sll r0, r0, 0 + c->vsub(DEST::x, vf23, vf24, vf1); // vsub.x vf23, vf24, vf1 + c->sqc2(vf23, 944, t3); // sqc2 vf23, 944(t3) + + block_72: + c->lw(a0, 3032, v1); // lw a0, 3032(v1) + c->mov64(t2, v1); // or t2, v1, r0 + c->lw(t3, 3028, v1); // lw t3, 3028(v1) + c->lqc2(vf23, 944, t2); // lqc2 vf23, 944(t2) + + block_73: + c->lbu(t4, 4, t3); // lbu t4, 4(t3) + c->daddiu(t3, t3, 1); // daddiu t3, t3, 1 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L71 + c->daddiu(t5, t4, -1); // daddiu t5, t4, -1 + if (bc) {goto block_131;} // branch non-likely + + bc = c->sgpr64(t5) == 0; // beq t5, r0, L65 + c->daddiu(t5, t4, -126); // daddiu t5, t4, -126 + if (bc) {goto block_121;} // branch non-likely + + bc = c->sgpr64(t5) != 0; // bne t5, r0, L66 + // nop // sll r0, r0, 0 + if (bc) {goto block_122;} // branch non-likely + + c->lbu(t4, 4, t3); // lbu t4, 4(t3) + c->daddiu(t3, t3, 1); // daddiu t3, t3, 1 + c->addiu(t5, r0, 0); // addiu t5, r0, 0 + c->addiu(t6, r0, 0); // addiu t6, r0, 0 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L71 + c->daddiu(t7, t4, -43); // daddiu t7, t4, -43 + if (bc) {goto block_131;} // branch non-likely + + c->movz(t5, t4, t7); // movz t5, t4, t7 + c->daddiu(t7, t4, -45); // daddiu t7, t4, -45 + c->movz(t5, t4, t7); // movz t5, t4, t7 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t5) != 0; // bne t5, r0, L52 + c->daddiu(t7, t4, -121); // daddiu t7, t4, -121 + if (bc) {goto block_84;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L63 + c->daddiu(t6, t4, -89); // daddiu t6, t4, -89 + if (bc) {goto block_119;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L63 + c->daddiu(t6, t4, -122); // daddiu t6, t4, -122 + if (bc) {goto block_119;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L64 + c->daddiu(t6, t4, -90); // daddiu t6, t4, -90 + if (bc) {goto block_120;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L64 + c->daddiu(t6, t4, -48); // daddiu t6, t4, -48 + if (bc) {goto block_120;} // branch non-likely + + bc = ((s64)c->sgpr64(t6)) < 0; // bltz t6, L66 + c->daddiu(t6, t4, -57); // daddiu t6, t4, -57 + if (bc) {goto block_122;} // branch non-likely + + bc = ((s64)c->sgpr64(t6)) > 0; // bgtz t6, L66 + c->daddiu(t6, t4, -48); // daddiu t6, t4, -48 + if (bc) {goto block_122;} // branch non-likely + + + block_84: + c->lbu(t4, 4, t3); // lbu t4, 4(t3) + c->daddiu(t3, t3, 1); // daddiu t3, t3, 1 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L71 + c->daddiu(t7, t4, -110); // daddiu t7, t4, -110 + if (bc) {goto block_131;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L53 + c->daddiu(t7, t4, -78); // daddiu t7, t4, -78 + if (bc) {goto block_102;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L53 + c->daddiu(t7, t4, -108); // daddiu t7, t4, -108 + if (bc) {goto block_102;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L51 + c->daddiu(t7, t4, -76); // daddiu t7, t4, -76 + if (bc) {goto block_73;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L51 + c->daddiu(t7, t4, -119); // daddiu t7, t4, -119 + if (bc) {goto block_73;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L55 + c->daddiu(t7, t4, -87); // daddiu t7, t4, -87 + if (bc) {goto block_105;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L55 + c->daddiu(t7, t4, -107); // daddiu t7, t4, -107 + if (bc) {goto block_105;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L56 + c->daddiu(t7, t4, -75); // daddiu t7, t4, -75 + if (bc) {goto block_107;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L56 + c->daddiu(t7, t4, -106); // daddiu t7, t4, -106 + if (bc) {goto block_107;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L51 + c->daddiu(t7, t4, -74); // daddiu t7, t4, -74 + if (bc) {goto block_73;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L51 + c->daddiu(t7, t4, -104); // daddiu t7, t4, -104 + if (bc) {goto block_73;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L57 + c->daddiu(t7, t4, -72); // daddiu t7, t4, -72 + if (bc) {goto block_109;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L57 + c->daddiu(t7, t4, -118); // daddiu t7, t4, -118 + if (bc) {goto block_109;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L60 + c->daddiu(t7, t4, -86); // daddiu t7, t4, -86 + if (bc) {goto block_114;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L60 + c->daddiu(t7, t4, -48); // daddiu t7, t4, -48 + if (bc) {goto block_114;} // branch non-likely + + bc = ((s64)c->sgpr64(t7)) < 0; // bltz t7, L66 + c->daddiu(t8, t4, -57); // daddiu t8, t4, -57 + if (bc) {goto block_122;} // branch non-likely + + bc = ((s64)c->sgpr64(t8)) > 0; // bgtz t8, L66 + c->sll(t8, t6, 2); // sll t8, t6, 2 + if (bc) {goto block_122;} // branch non-likely + + c->daddu(t4, t6, t8); // daddu t4, t6, t8 + // nop // sll r0, r0, 0 + c->sll(t4, t4, 1); // sll t4, t4, 1 + // nop // sll r0, r0, 0 + //beq r0, r0, L52 // beq r0, r0, L52 + c->daddu(t6, t4, t7); // daddu t6, t4, t7 + goto block_84; // branch always + + + block_102: + bc = c->sgpr64(t6) != 0; // bne t6, r0, L54 + c->load_symbol(a3, cache.font12_table); // lw a3, *font12-table*(s7) + if (bc) {goto block_104;} // branch non-likely + + c->mov64(a3, a3); // or a3, a3, r0 + c->addiu(t0, r0, -33); // addiu t0, r0, -33 + c->lqc2(vf13, 144, v1); // lqc2 vf13, 144(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf14, 160, v1); // lqc2 vf14, 160(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf15, 176, v1); // lqc2 vf15, 176(v1) + c->and_(a0, a0, t0); // and a0, a0, t0 + c->lq(t0, 80, v1); // lq t0, 80(v1) + //beq r0, r0, L51 // beq r0, r0, L51 + c->lq(t1, 96, v1); // lq t1, 96(v1) + goto block_73; // branch always + + + block_104: + // nop // sll r0, r0, 0 + c->load_symbol(a3, cache.font24_table); // lw a3, *font24-table*(s7) + c->mov64(a3, a3); // or a3, a3, r0 + // nop // sll r0, r0, 0 + c->lqc2(vf13, 192, v1); // lqc2 vf13, 192(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf14, 208, v1); // lqc2 vf14, 208(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf15, 224, v1); // lqc2 vf15, 224(v1) + c->ori(a0, a0, 32); // ori a0, a0, 32 + c->lq(t0, 112, v1); // lq t0, 112(v1) + //beq r0, r0, L51 // beq r0, r0, L51 + c->lq(t1, 128, v1); // lq t1, 128(v1) + goto block_73; // branch always + + + block_105: + c->addiu(t4, r0, -2); // addiu t4, r0, -2 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t6) == 0; // beq t6, r0, L51 + c->and_(a0, a0, t4); // and a0, a0, t4 + if (bc) {goto block_73;} // branch non-likely + + //beq r0, r0, L51 // beq r0, r0, L51 + c->ori(a0, a0, 1); // ori a0, a0, 1 + goto block_73; // branch always + + + block_107: + c->addiu(t4, r0, -3); // addiu t4, r0, -3 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t6) == 0; // beq t6, r0, L51 + c->and_(a0, a0, t4); // and a0, a0, t4 + if (bc) {goto block_73;} // branch non-likely + + //beq r0, r0, L51 // beq r0, r0, L51 + c->ori(a0, a0, 2); // ori a0, a0, 2 + goto block_73; // branch always + + + block_109: + c->mov128_vf_gpr(vf1, t6); // qmtc2.i vf1, t6 + c->daddiu(t4, t5, -45); // daddiu t4, t5, -45 + bc = c->sgpr64(t5) == 0; // beq t5, r0, L59 + c->vitof0(DEST::xyzw, vf1, vf1); // vitof0.xyzw vf1, vf1 + if (bc) {goto block_113;} // branch non-likely + + bc = c->sgpr64(t4) == 0; // beq t4, r0, L58 + // nop // sll r0, r0, 0 + if (bc) {goto block_112;} // branch non-likely + + //beq r0, r0, L51 // beq r0, r0, L51 + c->vadd_bc(DEST::x, BC::x, vf23, vf23, vf1); // vaddx.x vf23, vf23, vf1 + goto block_73; // branch always + + + block_112: + //beq r0, r0, L51 // beq r0, r0, L51 + c->vsub_bc(DEST::x, BC::x, vf23, vf23, vf1); // vsubx.x vf23, vf23, vf1 + goto block_73; // branch always + + + block_113: + //beq r0, r0, L51 // beq r0, r0, L51 + c->vadd_bc(DEST::x, BC::x, vf23, vf0, vf1); // vaddx.x vf23, vf0, vf1 + goto block_73; // branch always + + + block_114: + c->mov128_vf_gpr(vf1, t6); // qmtc2.i vf1, t6 + c->daddiu(t4, t5, -45); // daddiu t4, t5, -45 + bc = c->sgpr64(t5) == 0; // beq t5, r0, L62 + c->vitof0(DEST::xyzw, vf1, vf1); // vitof0.xyzw vf1, vf1 + if (bc) {goto block_118;} // branch non-likely + + bc = c->sgpr64(t4) == 0; // beq t4, r0, L61 + // nop // sll r0, r0, 0 + if (bc) {goto block_117;} // branch non-likely + + //beq r0, r0, L51 // beq r0, r0, L51 + c->vadd_bc(DEST::y, BC::x, vf23, vf23, vf1); // vaddx.y vf23, vf23, vf1 + goto block_73; // branch always + + + block_117: + //beq r0, r0, L51 // beq r0, r0, L51 + c->vsub_bc(DEST::y, BC::x, vf23, vf23, vf1); // vsubx.y vf23, vf23, vf1 + goto block_73; // branch always + + + block_118: + //beq r0, r0, L51 // beq r0, r0, L51 + c->vadd_bc(DEST::y, BC::x, vf23, vf0, vf1); // vaddx.y vf23, vf0, vf1 + goto block_73; // branch always + + + block_119: + //beq r0, r0, L51 // beq r0, r0, L51 + c->sqc2(vf23, 288, v1); // sqc2 vf23, 288(v1) + goto block_73; // branch always + + + block_120: + //beq r0, r0, L51 // beq r0, r0, L51 + c->lqc2(vf23, 288, v1); // lqc2 vf23, 288(v1) + goto block_73; // branch always + + + block_121: + c->lbu(t4, 4, t3); // lbu t4, 4(t3) + c->daddiu(t3, t3, 1); // daddiu t3, t3, 1 + c->vadd(DEST::xyz, vf4, vf23, vf15); // vadd.xyz vf4, vf23, vf15 + c->andi(t5, t4, 127); // andi t5, t4, 127 + c->vsub(DEST::xyzw, vf1, vf25, vf23); // vsub.xyzw vf1, vf25, vf23 + c->daddiu(t5, t5, 255); // daddiu t5, t5, 255 + //beq r0, r0, L68 // beq r0, r0, L68 + c->sll(t5, t5, 4); // sll t5, t5, 4 + goto block_125; // branch always + + + block_122: + c->vadd(DEST::xyz, vf4, vf23, vf15); // vadd.xyz vf4, vf23, vf15 + c->sll(t5, t4, 4); // sll t5, t4, 4 + c->vsub(DEST::xyzw, vf1, vf25, vf23); // vsub.xyzw vf1, vf25, vf23 + c->daddiu(t6, t4, -10); // daddiu t6, t4, -10 + bc = c->sgpr64(t6) == 0; // beq t6, r0, L67 + c->daddiu(t6, t4, -13); // daddiu t6, t4, -13 + if (bc) {goto block_124;} // branch non-likely + + bc = c->sgpr64(t6) != 0; // bne t6, r0, L68 + c->andi(t6, a0, 1); // andi t6, a0, 1 + if (bc) {goto block_125;} // branch non-likely + + + block_124: + c->daddiu(t2, t2, 16); // daddiu t2, t2, 16 + // nop // sll r0, r0, 0 + //beq r0, r0, L51 // beq r0, r0, L51 + c->lqc2(vf23, 944, t2); // lqc2 vf23, 944(t2) + goto block_73; // branch always + + + block_125: + c->addu(t5, t5, a3); // addu t5, t5, a3 + // nop // sll r0, r0, 0 + c->lqc2(vf5, -256, t5); // lqc2 vf5, -256(t5) + c->mov128_gpr_vf(t5, vf1); // qmfc2.i t5, vf1 + bc = ((s64)c->sgpr64(t5)) < 0; // bltz t5, L71 + c->vadd(DEST::xyz, vf6, vf5, vf16); // vadd.xyz vf6, vf5, vf16 + if (bc) {goto block_131;} // branch non-likely + + c->sra(t5, t5, 31); // sra t5, t5, 31 + c->vadd(DEST::xyz, vf7, vf5, vf17); // vadd.xyz vf7, vf5, vf17 + bc = ((s64)c->sgpr64(t5)) < 0; // bltz t5, L51 + c->vadd(DEST::xyz, vf8, vf5, vf18); // vadd.xyz vf8, vf5, vf18 + if (bc) {goto block_73;} // branch non-likely + + c->vadd(DEST::xyz, vf1, vf23, vf0); // vadd.xyz vf1, vf23, vf0 + // nop // sll r0, r0, 0 + c->vadd(DEST::xyz, vf2, vf23, vf13); // vadd.xyz vf2, vf23, vf13 + c->sqc2(vf5, 496, v1); // sqc2 vf5, 496(v1) + c->vadd(DEST::xyz, vf3, vf23, vf14); // vadd.xyz vf3, vf23, vf14 + c->sqc2(vf6, 512, v1); // sqc2 vf6, 512(v1) + c->vmul(DEST::xyzw, vf19, vf5, vf13); // vmul.xyzw vf19, vf5, vf13 + c->sqc2(vf7, 528, v1); // sqc2 vf7, 528(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf8, 544, v1); // sqc2 vf8, 544(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf1, 368, v1); // sqc2 vf1, 368(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf2, 384, v1); // sqc2 vf2, 384(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf3, 400, v1); // sqc2 vf3, 400(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf4, 416, v1); // sqc2 vf4, 416(v1) + c->lqc2(vf1, 368, v1); // lqc2 vf1, 368(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf2, 384, v1); // lqc2 vf2, 384(v1) + c->vmula_bc(DEST::xyzw, BC::w, vf31, vf0); // vmulaw.xyzw acc, vf31, vf0 + c->lqc2(vf3, 400, v1); // lqc2 vf3, 400(v1) + c->vmadda_bc(DEST::xyzw, BC::x, vf28, vf1); // vmaddax.xyzw acc, vf28, vf1 + c->lqc2(vf4, 416, v1); // lqc2 vf4, 416(v1) + c->vmadda_bc(DEST::xyzw, BC::y, vf29, vf1); // vmadday.xyzw acc, vf29, vf1 + // nop // sll r0, r0, 0 + c->vmadd_bc(DEST::xyzw, BC::z, vf1, vf30, vf1); // vmaddz.xyzw vf1, vf30, vf1 + // nop // sll r0, r0, 0 + c->vmula_bc(DEST::xyzw, BC::w, vf31, vf0); // vmulaw.xyzw acc, vf31, vf0 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::x, vf28, vf2); // vmaddax.xyzw acc, vf28, vf2 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::y, vf29, vf2); // vmadday.xyzw acc, vf29, vf2 + // nop // sll r0, r0, 0 + c->vmadd_bc(DEST::xyzw, BC::z, vf2, vf30, vf2); // vmaddz.xyzw vf2, vf30, vf2 + // nop // sll r0, r0, 0 + c->vmula_bc(DEST::xyzw, BC::w, vf31, vf0); // vmulaw.xyzw acc, vf31, vf0 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::x, vf28, vf3); // vmaddax.xyzw acc, vf28, vf3 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::y, vf29, vf3); // vmadday.xyzw acc, vf29, vf3 + // nop // sll r0, r0, 0 + c->vmadd_bc(DEST::xyzw, BC::z, vf3, vf30, vf3); // vmaddz.xyzw vf3, vf30, vf3 + // nop // sll r0, r0, 0 + c->vmula_bc(DEST::xyzw, BC::w, vf31, vf0); // vmulaw.xyzw acc, vf31, vf0 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::x, vf28, vf4); // vmaddax.xyzw acc, vf28, vf4 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::y, vf29, vf4); // vmadday.xyzw acc, vf29, vf4 + // nop // sll r0, r0, 0 + c->vmadd_bc(DEST::xyzw, BC::z, vf4, vf30, vf4); // vmaddz.xyzw vf4, vf30, vf4 + c->vdiv(vf25, BC::z, vf1, BC::w); // vdiv Q, vf25.z, vf1.w + c->lq(t5, 32, v1); // lq t5, 32(v1) + // nop // sll r0, r0, 0 + c->lq(t6, 48, v1); // lq t6, 48(v1) + // nop // sll r0, r0, 0 + c->sq(t5, 0, a1); // sq t5, 0(a1) + // nop // sll r0, r0, 0 + c->sq(t6, 16, a1); // sq t6, 16(a1) + c->lqc2(vf5, 496, v1); // lqc2 vf5, 496(v1) + c->mov128_gpr_gpr(t5, t0); // por t5, t0, r0 + c->lqc2(vf6, 512, v1); // lqc2 vf6, 512(v1) + c->andi(t4, t4, 128); // andi t4, t4, 128 + c->lqc2(vf7, 528, v1); // lqc2 vf7, 528(v1) + c->movn(t5, t1, t4); // movn t5, t1, t4 + c->vmulq(DEST::xyz, vf1, vf1); // vmulq.xyz vf1, vf1, Q + c->sq(t5, 32, a1); // sq t5, 32(a1) + c->vmulq(DEST::xyz, vf5, vf5); // vmulq.xyz vf5, vf5, Q + // nop // sll r0, r0, 0 + // nop // vnop + // nop // sll r0, r0, 0 + // nop // vnop + // nop // sll r0, r0, 0 + c->vdiv(vf25, BC::z, vf2, BC::w); // vdiv Q, vf25.z, vf2.w + // nop // sll r0, r0, 0 + c->lqc2(vf8, 544, v1); // lqc2 vf8, 544(v1) + // nop // sll r0, r0, 0 + c->vadd(DEST::xyzw, vf1, vf1, vf26); // vadd.xyzw vf1, vf1, vf26 + // nop // sll r0, r0, 0 + c->lqc2(vf9, 1968, v1); // lqc2 vf9, 1968(v1) + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + c->vftoi4(DEST::xyzw, vf1, vf1); // vftoi4.xyzw vf1, vf1 + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf2, vf2); // vmulq.xyz vf2, vf2, Q + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf6, vf6); // vmulq.xyz vf6, vf6, Q + c->sqc2(vf5, 48, a1); // sqc2 vf5, 48(a1) + // nop // vnop + // nop // sll r0, r0, 0 + // nop // vnop + // nop // sll r0, r0, 0 + c->vdiv(vf25, BC::z, vf3, BC::w); // vdiv Q, vf25.z, vf3.w + c->sqc2(vf9, 64, a1); // sqc2 vf9, 64(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf1, 80, a1); // sqc2 vf1, 80(a1) + c->vadd(DEST::xyzw, vf2, vf2, vf26); // vadd.xyzw vf2, vf2, vf26 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + c->vftoi4(DEST::xyzw, vf2, vf2); // vftoi4.xyzw vf2, vf2 + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf3, vf3); // vmulq.xyz vf3, vf3, Q + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf7, vf7); // vmulq.xyz vf7, vf7, Q + c->sqc2(vf6, 96, a1); // sqc2 vf6, 96(a1) + // nop // vnop + // nop // sll r0, r0, 0 + // nop // vnop + // nop // sll r0, r0, 0 + c->vdiv(vf25, BC::z, vf4, BC::w); // vdiv Q, vf25.z, vf4.w + c->sqc2(vf9, 112, a1); // sqc2 vf9, 112(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf2, 128, a1); // sqc2 vf2, 128(a1) + c->vadd(DEST::xyzw, vf3, vf3, vf26); // vadd.xyzw vf3, vf3, vf26 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + c->vftoi4(DEST::xyzw, vf3, vf3); // vftoi4.xyzw vf3, vf3 + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf4, vf4); // vmulq.xyz vf4, vf4, Q + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf8, vf8); // vmulq.xyz vf8, vf8, Q + c->sqc2(vf7, 144, a1); // sqc2 vf7, 144(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf9, 160, a1); // sqc2 vf9, 160(a1) + c->vadd(DEST::xyzw, vf4, vf4, vf26); // vadd.xyzw vf4, vf4, vf26 + c->sqc2(vf3, 176, a1); // sqc2 vf3, 176(a1) + c->andi(t4, a0, 2); // andi t4, a0, 2 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L69 + // nop // sll r0, r0, 0 + if (bc) {goto block_129;} // branch non-likely + + //beq r0, r0, L70 // beq r0, r0, L70 + c->vadd_bc(DEST::x, BC::w, vf23, vf23, vf19); // vaddw.x vf23, vf23, vf19 + goto block_130; // branch always + + + block_129: + // nop // sll r0, r0, 0 + c->vadd_bc(DEST::x, BC::w, vf23, vf23, vf14); // vaddw.x vf23, vf23, vf14 + + block_130: + c->vftoi4(DEST::xyzw, vf4, vf4); // vftoi4.xyzw vf4, vf4 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + c->sqc2(vf8, 192, a1); // sqc2 vf8, 192(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf9, 208, a1); // sqc2 vf9, 208(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf4, 224, a1); // sqc2 vf4, 224(a1) + //beq r0, r0, L51 // beq r0, r0, L51 + c->daddiu(a1, a1, 240); // daddiu a1, a1, 240 + goto block_73; // branch always + + + block_131: + c->lw(a0, 3032, v1); // lw a0, 3032(v1) + c->mov64(t2, v1); // or t2, v1, r0 + c->lw(t3, 3028, v1); // lw t3, 3028(v1) + c->lqc2(vf23, 944, t2); // lqc2 vf23, 944(t2) + + block_132: + c->lbu(t4, 4, t3); // lbu t4, 4(t3) + c->daddiu(t3, t3, 1); // daddiu t3, t3, 1 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L92 + c->daddiu(t5, t4, -1); // daddiu t5, t4, -1 + if (bc) {goto block_189;} // branch non-likely + + bc = c->sgpr64(t5) == 0; // beq t5, r0, L86 + c->daddiu(t5, t4, -126); // daddiu t5, t4, -126 + if (bc) {goto block_179;} // branch non-likely + + bc = c->sgpr64(t5) != 0; // bne t5, r0, L87 + // nop // sll r0, r0, 0 + if (bc) {goto block_180;} // branch non-likely + + c->lbu(t4, 4, t3); // lbu t4, 4(t3) + c->daddiu(t3, t3, 1); // daddiu t3, t3, 1 + c->addiu(t5, r0, 0); // addiu t5, r0, 0 + c->addiu(t6, r0, 0); // addiu t6, r0, 0 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L92 + c->daddiu(t7, t4, -43); // daddiu t7, t4, -43 + if (bc) {goto block_189;} // branch non-likely + + c->movz(t5, t4, t7); // movz t5, t4, t7 + c->daddiu(t7, t4, -45); // daddiu t7, t4, -45 + c->movz(t5, t4, t7); // movz t5, t4, t7 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t5) != 0; // bne t5, r0, L73 + c->daddiu(t7, t4, -121); // daddiu t7, t4, -121 + if (bc) {goto block_143;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L84 + c->daddiu(t6, t4, -89); // daddiu t6, t4, -89 + if (bc) {goto block_177;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L84 + c->daddiu(t6, t4, -122); // daddiu t6, t4, -122 + if (bc) {goto block_177;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L85 + c->daddiu(t6, t4, -90); // daddiu t6, t4, -90 + if (bc) {goto block_178;} // branch non-likely + + bc = c->sgpr64(t6) == 0; // beq t6, r0, L85 + c->daddiu(t6, t4, -48); // daddiu t6, t4, -48 + if (bc) {goto block_178;} // branch non-likely + + bc = ((s64)c->sgpr64(t6)) < 0; // bltz t6, L87 + c->daddiu(t6, t4, -57); // daddiu t6, t4, -57 + if (bc) {goto block_180;} // branch non-likely + + bc = ((s64)c->sgpr64(t6)) > 0; // bgtz t6, L87 + c->daddiu(t6, t4, -48); // daddiu t6, t4, -48 + if (bc) {goto block_180;} // branch non-likely + + + block_143: + c->lbu(t4, 4, t3); // lbu t4, 4(t3) + c->daddiu(t3, t3, 1); // daddiu t3, t3, 1 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L92 + c->daddiu(t7, t4, -110); // daddiu t7, t4, -110 + if (bc) {goto block_189;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L74 + c->daddiu(t7, t4, -78); // daddiu t7, t4, -78 + if (bc) {goto block_161;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L74 + c->daddiu(t7, t4, -108); // daddiu t7, t4, -108 + if (bc) {goto block_161;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L76 + c->daddiu(t7, t4, -76); // daddiu t7, t4, -76 + if (bc) {goto block_164;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L76 + c->daddiu(t7, t4, -119); // daddiu t7, t4, -119 + if (bc) {goto block_164;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L72 + c->daddiu(t7, t4, -87); // daddiu t7, t4, -87 + if (bc) {goto block_132;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L72 + c->daddiu(t7, t4, -107); // daddiu t7, t4, -107 + if (bc) {goto block_132;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L77 + c->daddiu(t7, t4, -75); // daddiu t7, t4, -75 + if (bc) {goto block_165;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L77 + c->daddiu(t7, t4, -106); // daddiu t7, t4, -106 + if (bc) {goto block_165;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L72 + c->daddiu(t7, t4, -74); // daddiu t7, t4, -74 + if (bc) {goto block_132;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L72 + c->daddiu(t7, t4, -104); // daddiu t7, t4, -104 + if (bc) {goto block_132;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L78 + c->daddiu(t7, t4, -72); // daddiu t7, t4, -72 + if (bc) {goto block_167;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L78 + c->daddiu(t7, t4, -118); // daddiu t7, t4, -118 + if (bc) {goto block_167;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L81 + c->daddiu(t7, t4, -86); // daddiu t7, t4, -86 + if (bc) {goto block_172;} // branch non-likely + + bc = c->sgpr64(t7) == 0; // beq t7, r0, L81 + c->daddiu(t7, t4, -48); // daddiu t7, t4, -48 + if (bc) {goto block_172;} // branch non-likely + + bc = ((s64)c->sgpr64(t7)) < 0; // bltz t7, L87 + c->daddiu(t8, t4, -57); // daddiu t8, t4, -57 + if (bc) {goto block_180;} // branch non-likely + + bc = ((s64)c->sgpr64(t8)) > 0; // bgtz t8, L87 + c->sll(t8, t6, 2); // sll t8, t6, 2 + if (bc) {goto block_180;} // branch non-likely + + c->daddu(t4, t6, t8); // daddu t4, t6, t8 + // nop // sll r0, r0, 0 + c->sll(t4, t4, 1); // sll t4, t4, 1 + // nop // sll r0, r0, 0 + //beq r0, r0, L73 // beq r0, r0, L73 + c->daddu(t6, t4, t7); // daddu t6, t4, t7 + goto block_143; // branch always + + + block_161: + bc = c->sgpr64(t6) != 0; // bne t6, r0, L75 + c->load_symbol(a3, cache.font12_table); // lw a3, *font12-table*(s7) + if (bc) {goto block_163;} // branch non-likely + + c->mov64(a3, a3); // or a3, a3, r0 + c->addiu(t0, r0, -33); // addiu t0, r0, -33 + c->lqc2(vf13, 144, v1); // lqc2 vf13, 144(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf14, 160, v1); // lqc2 vf14, 160(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf15, 176, v1); // lqc2 vf15, 176(v1) + c->and_(a0, a0, t0); // and a0, a0, t0 + c->lq(t0, 80, v1); // lq t0, 80(v1) + //beq r0, r0, L72 // beq r0, r0, L72 + c->lq(t1, 96, v1); // lq t1, 96(v1) + goto block_132; // branch always + + + block_163: + // nop // sll r0, r0, 0 + c->load_symbol(a3, cache.font24_table); // lw a3, *font24-table*(s7) + c->mov64(a3, a3); // or a3, a3, r0 + // nop // sll r0, r0, 0 + c->lqc2(vf13, 192, v1); // lqc2 vf13, 192(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf14, 208, v1); // lqc2 vf14, 208(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf15, 224, v1); // lqc2 vf15, 224(v1) + c->ori(a0, a0, 32); // ori a0, a0, 32 + c->lq(t0, 112, v1); // lq t0, 112(v1) + //beq r0, r0, L72 // beq r0, r0, L72 + c->lq(t1, 128, v1); // lq t1, 128(v1) + goto block_132; // branch always + + + block_164: + // nop // sll r0, r0, 0 + c->sw(t6, 3008, v1); // sw t6, 3008(v1) + c->sll(t4, t6, 4); // sll t4, t6, 4 + c->lq(t5, 432, v1); // lq t5, 432(v1) + c->daddu(t4, t4, v1); // daddu t4, t4, v1 + c->lq(t6, 448, v1); // lq t6, 448(v1) + // nop // sll r0, r0, 0 + c->sq(t5, 624, v1); // sq t5, 624(v1) + // nop // sll r0, r0, 0 + c->sq(t6, 640, v1); // sq t6, 640(v1) + // nop // sll r0, r0, 0 + c->lq(t5, 464, v1); // lq t5, 464(v1) + // nop // sll r0, r0, 0 + c->lq(t6, 480, v1); // lq t6, 480(v1) + // nop // sll r0, r0, 0 + c->sq(t5, 656, v1); // sq t5, 656(v1) + // nop // sll r0, r0, 0 + c->sq(t6, 672, v1); // sq t6, 672(v1) + // nop // sll r0, r0, 0 + c->lwu(t6, 1984, t4); // lwu t6, 1984(t4) + // nop // sll r0, r0, 0 + c->lwu(t5, 1988, t4); // lwu t5, 1988(t4) + c->pextlb(t6, r0, t6); // pextlb t6, r0, t6 + c->mfc1(r0, f31); // mfc1 r0, f31 + c->pextlh(t6, r0, t6); // pextlh t6, r0, t6 + c->mfc1(r0, f31); // mfc1 r0, f31 + c->pextlb(t5, r0, t5); // pextlb t5, r0, t5 + c->sq(t6, 432, v1); // sq t6, 432(v1) + c->pextlh(t5, r0, t5); // pextlh t5, r0, t5 + c->sq(t6, 816, v1); // sq t6, 816(v1) + // nop // sll r0, r0, 0 + c->sq(t5, 448, v1); // sq t5, 448(v1) + // nop // sll r0, r0, 0 + c->sq(t5, 832, v1); // sq t5, 832(v1) + // nop // sll r0, r0, 0 + c->lwu(t5, 1992, t4); // lwu t5, 1992(t4) + // nop // sll r0, r0, 0 + c->lwu(t4, 1996, t4); // lwu t4, 1996(t4) + c->pextlb(t5, r0, t5); // pextlb t5, r0, t5 + c->mfc1(r0, f31); // mfc1 r0, f31 + c->pextlh(t5, r0, t5); // pextlh t5, r0, t5 + c->mfc1(r0, f31); // mfc1 r0, f31 + c->pextlb(t4, r0, t4); // pextlb t4, r0, t4 + c->sq(t5, 464, v1); // sq t5, 464(v1) + c->pextlh(t4, r0, t4); // pextlh t4, r0, t4 + c->sq(t5, 848, v1); // sq t5, 848(v1) + // nop // sll r0, r0, 0 + c->sq(t4, 480, v1); // sq t4, 480(v1) + //beq r0, r0, L72 // beq r0, r0, L72 + c->sq(t4, 864, v1); // sq t4, 864(v1) + goto block_132; // branch always + + + block_165: + c->addiu(t4, r0, -3); // addiu t4, r0, -3 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t6) == 0; // beq t6, r0, L72 + c->and_(a0, a0, t4); // and a0, a0, t4 + if (bc) {goto block_132;} // branch non-likely + + //beq r0, r0, L72 // beq r0, r0, L72 + c->ori(a0, a0, 2); // ori a0, a0, 2 + goto block_132; // branch always + + + block_167: + c->mov128_vf_gpr(vf1, t6); // qmtc2.i vf1, t6 + c->daddiu(t4, t5, -45); // daddiu t4, t5, -45 + bc = c->sgpr64(t5) == 0; // beq t5, r0, L80 + c->vitof0(DEST::xyzw, vf1, vf1); // vitof0.xyzw vf1, vf1 + if (bc) {goto block_171;} // branch non-likely + + bc = c->sgpr64(t4) == 0; // beq t4, r0, L79 + // nop // sll r0, r0, 0 + if (bc) {goto block_170;} // branch non-likely + + //beq r0, r0, L72 // beq r0, r0, L72 + c->vadd_bc(DEST::x, BC::x, vf23, vf23, vf1); // vaddx.x vf23, vf23, vf1 + goto block_132; // branch always + + + block_170: + //beq r0, r0, L72 // beq r0, r0, L72 + c->vsub_bc(DEST::x, BC::x, vf23, vf23, vf1); // vsubx.x vf23, vf23, vf1 + goto block_132; // branch always + + + block_171: + //beq r0, r0, L72 // beq r0, r0, L72 + c->vadd_bc(DEST::x, BC::x, vf23, vf0, vf1); // vaddx.x vf23, vf0, vf1 + goto block_132; // branch always + + + block_172: + c->mov128_vf_gpr(vf1, t6); // qmtc2.i vf1, t6 + c->daddiu(t4, t5, -45); // daddiu t4, t5, -45 + bc = c->sgpr64(t5) == 0; // beq t5, r0, L83 + c->vitof0(DEST::xyzw, vf1, vf1); // vitof0.xyzw vf1, vf1 + if (bc) {goto block_176;} // branch non-likely + + bc = c->sgpr64(t4) == 0; // beq t4, r0, L82 + // nop // sll r0, r0, 0 + if (bc) {goto block_175;} // branch non-likely + + //beq r0, r0, L72 // beq r0, r0, L72 + c->vadd_bc(DEST::y, BC::x, vf23, vf23, vf1); // vaddx.y vf23, vf23, vf1 + goto block_132; // branch always + + + block_175: + //beq r0, r0, L72 // beq r0, r0, L72 + c->vsub_bc(DEST::y, BC::x, vf23, vf23, vf1); // vsubx.y vf23, vf23, vf1 + goto block_132; // branch always + + + block_176: + //beq r0, r0, L72 // beq r0, r0, L72 + c->vadd_bc(DEST::y, BC::x, vf23, vf0, vf1); // vaddx.y vf23, vf0, vf1 + goto block_132; // branch always + + + block_177: + // nop // sll r0, r0, 0 + c->lw(t4, 3008, v1); // lw t4, 3008(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf9, 432, v1); // lqc2 vf9, 432(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf10, 448, v1); // lqc2 vf10, 448(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf11, 464, v1); // lqc2 vf11, 464(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf12, 480, v1); // lqc2 vf12, 480(v1) + // nop // sll r0, r0, 0 + c->sw(t4, 3016, v1); // sw t4, 3016(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf9, 304, v1); // sqc2 vf9, 304(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf10, 320, v1); // sqc2 vf10, 320(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf11, 336, v1); // sqc2 vf11, 336(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf12, 352, v1); // sqc2 vf12, 352(v1) + //beq r0, r0, L72 // beq r0, r0, L72 + c->sqc2(vf23, 288, v1); // sqc2 vf23, 288(v1) + goto block_132; // branch always + + + block_178: + // nop // sll r0, r0, 0 + c->lw(t4, 3016, v1); // lw t4, 3016(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf9, 304, v1); // lqc2 vf9, 304(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf10, 320, v1); // lqc2 vf10, 320(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf11, 336, v1); // lqc2 vf11, 336(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf12, 352, v1); // lqc2 vf12, 352(v1) + // nop // sll r0, r0, 0 + c->sw(t4, 3008, v1); // sw t4, 3008(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf9, 432, v1); // sqc2 vf9, 432(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf9, 816, v1); // sqc2 vf9, 816(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf10, 448, v1); // sqc2 vf10, 448(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf10, 832, v1); // sqc2 vf10, 832(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf11, 464, v1); // sqc2 vf11, 464(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf11, 848, v1); // sqc2 vf11, 848(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf12, 480, v1); // sqc2 vf12, 480(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf12, 864, v1); // sqc2 vf12, 864(v1) + //beq r0, r0, L72 // beq r0, r0, L72 + c->lqc2(vf23, 288, v1); // lqc2 vf23, 288(v1) + goto block_132; // branch always + + + block_179: + c->lbu(t4, 4, t3); // lbu t4, 4(t3) + c->daddiu(t3, t3, 1); // daddiu t3, t3, 1 + c->vadd(DEST::xyz, vf4, vf23, vf15); // vadd.xyz vf4, vf23, vf15 + c->andi(t5, t4, 127); // andi t5, t4, 127 + c->vsub(DEST::xyzw, vf1, vf25, vf23); // vsub.xyzw vf1, vf25, vf23 + c->daddiu(t5, t5, 255); // daddiu t5, t5, 255 + //beq r0, r0, L89 // beq r0, r0, L89 + c->sll(t5, t5, 4); // sll t5, t5, 4 + goto block_183; // branch always + + + block_180: + c->vadd(DEST::xyz, vf4, vf23, vf15); // vadd.xyz vf4, vf23, vf15 + c->sll(t5, t4, 4); // sll t5, t4, 4 + c->vsub(DEST::xyzw, vf1, vf25, vf23); // vsub.xyzw vf1, vf25, vf23 + c->daddiu(t6, t4, -10); // daddiu t6, t4, -10 + bc = c->sgpr64(t6) == 0; // beq t6, r0, L88 + c->daddiu(t6, t4, -13); // daddiu t6, t4, -13 + if (bc) {goto block_182;} // branch non-likely + + bc = c->sgpr64(t6) != 0; // bne t6, r0, L89 + // nop // sll r0, r0, 0 + if (bc) {goto block_183;} // branch non-likely + + + block_182: + c->daddiu(t2, t2, 16); // daddiu t2, t2, 16 + // nop // sll r0, r0, 0 + //beq r0, r0, L72 // beq r0, r0, L72 + c->lqc2(vf23, 944, t2); // lqc2 vf23, 944(t2) + goto block_132; // branch always + + + block_183: + c->addu(t5, t5, a3); // addu t5, t5, a3 + // nop // sll r0, r0, 0 + c->lqc2(vf5, -256, t5); // lqc2 vf5, -256(t5) + c->mov128_gpr_vf(t5, vf1); // qmfc2.i t5, vf1 + bc = ((s64)c->sgpr64(t5)) < 0; // bltz t5, L92 + c->vadd(DEST::xyz, vf6, vf5, vf16); // vadd.xyz vf6, vf5, vf16 + if (bc) {goto block_189;} // branch non-likely + + c->sra(t5, t5, 31); // sra t5, t5, 31 + c->vadd(DEST::xyz, vf7, vf5, vf17); // vadd.xyz vf7, vf5, vf17 + bc = ((s64)c->sgpr64(t5)) < 0; // bltz t5, L72 + c->vadd(DEST::xyz, vf8, vf5, vf18); // vadd.xyz vf8, vf5, vf18 + if (bc) {goto block_132;} // branch non-likely + + c->vadd(DEST::xyz, vf1, vf23, vf0); // vadd.xyz vf1, vf23, vf0 + // nop // sll r0, r0, 0 + c->vadd(DEST::xyz, vf2, vf23, vf13); // vadd.xyz vf2, vf23, vf13 + // nop // sll r0, r0, 0 + c->vadd(DEST::xyz, vf3, vf23, vf14); // vadd.xyz vf3, vf23, vf14 + c->sqc2(vf5, 496, v1); // sqc2 vf5, 496(v1) + c->vadd(DEST::xyz, vf4, vf23, vf15); // vadd.xyz vf4, vf23, vf15 + c->sqc2(vf6, 512, v1); // sqc2 vf6, 512(v1) + c->vmul(DEST::xyzw, vf19, vf5, vf13); // vmul.xyzw vf19, vf5, vf13 + c->sqc2(vf7, 528, v1); // sqc2 vf7, 528(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf8, 544, v1); // sqc2 vf8, 544(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf1, 368, v1); // sqc2 vf1, 368(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf2, 384, v1); // sqc2 vf2, 384(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf3, 400, v1); // sqc2 vf3, 400(v1) + // nop // sll r0, r0, 0 + c->sqc2(vf4, 416, v1); // sqc2 vf4, 416(v1) + c->lqc2(vf1, 368, v1); // lqc2 vf1, 368(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf2, 384, v1); // lqc2 vf2, 384(v1) + c->vmula_bc(DEST::xyzw, BC::w, vf31, vf0); // vmulaw.xyzw acc, vf31, vf0 + c->lqc2(vf3, 400, v1); // lqc2 vf3, 400(v1) + c->vmadda_bc(DEST::xyzw, BC::x, vf28, vf1); // vmaddax.xyzw acc, vf28, vf1 + c->lqc2(vf4, 416, v1); // lqc2 vf4, 416(v1) + c->vmadda_bc(DEST::xyzw, BC::y, vf29, vf1); // vmadday.xyzw acc, vf29, vf1 + // nop // sll r0, r0, 0 + c->vmadd_bc(DEST::xyzw, BC::z, vf1, vf30, vf1); // vmaddz.xyzw vf1, vf30, vf1 + // nop // sll r0, r0, 0 + c->vmula_bc(DEST::xyzw, BC::w, vf31, vf0); // vmulaw.xyzw acc, vf31, vf0 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::x, vf28, vf2); // vmaddax.xyzw acc, vf28, vf2 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::y, vf29, vf2); // vmadday.xyzw acc, vf29, vf2 + // nop // sll r0, r0, 0 + c->vmadd_bc(DEST::xyzw, BC::z, vf2, vf30, vf2); // vmaddz.xyzw vf2, vf30, vf2 + // nop // sll r0, r0, 0 + c->vmula_bc(DEST::xyzw, BC::w, vf31, vf0); // vmulaw.xyzw acc, vf31, vf0 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::x, vf28, vf3); // vmaddax.xyzw acc, vf28, vf3 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::y, vf29, vf3); // vmadday.xyzw acc, vf29, vf3 + // nop // sll r0, r0, 0 + c->vmadd_bc(DEST::xyzw, BC::z, vf3, vf30, vf3); // vmaddz.xyzw vf3, vf30, vf3 + // nop // sll r0, r0, 0 + c->vmula_bc(DEST::xyzw, BC::w, vf31, vf0); // vmulaw.xyzw acc, vf31, vf0 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::x, vf28, vf4); // vmaddax.xyzw acc, vf28, vf4 + // nop // sll r0, r0, 0 + c->vmadda_bc(DEST::xyzw, BC::y, vf29, vf4); // vmadday.xyzw acc, vf29, vf4 + // nop // sll r0, r0, 0 + c->vmadd_bc(DEST::xyzw, BC::z, vf4, vf30, vf4); // vmaddz.xyzw vf4, vf30, vf4 + c->vdiv(vf25, BC::z, vf1, BC::w); // vdiv Q, vf25.z, vf1.w + c->lq(t5, 32, v1); // lq t5, 32(v1) + // nop // sll r0, r0, 0 + c->lq(t6, 48, v1); // lq t6, 48(v1) + // nop // sll r0, r0, 0 + c->sq(t5, 0, a1); // sq t5, 0(a1) + // nop // sll r0, r0, 0 + c->sq(t6, 16, a1); // sq t6, 16(a1) + c->lqc2(vf5, 496, v1); // lqc2 vf5, 496(v1) + c->mov128_gpr_gpr(t5, t0); // por t5, t0, r0 + c->lqc2(vf6, 512, v1); // lqc2 vf6, 512(v1) + c->andi(t4, t4, 128); // andi t4, t4, 128 + c->lqc2(vf7, 528, v1); // lqc2 vf7, 528(v1) + c->movn(t5, t1, t4); // movn t5, t1, t4 + c->vmulq(DEST::xyz, vf1, vf1); // vmulq.xyz vf1, vf1, Q + c->sq(t5, 32, a1); // sq t5, 32(a1) + c->vmulq(DEST::xyz, vf5, vf5); // vmulq.xyz vf5, vf5, Q + // nop // sll r0, r0, 0 + // nop // vnop + // nop // sll r0, r0, 0 + // nop // vnop + // nop // sll r0, r0, 0 + c->vdiv(vf25, BC::z, vf2, BC::w); // vdiv Q, vf25.z, vf2.w + // nop // sll r0, r0, 0 + c->lqc2(vf8, 544, v1); // lqc2 vf8, 544(v1) + // nop // sll r0, r0, 0 + c->vadd(DEST::xyzw, vf1, vf1, vf27); // vadd.xyzw vf1, vf1, vf27 + // nop // sll r0, r0, 0 + c->lqc2(vf9, 432, v1); // lqc2 vf9, 432(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf10, 448, v1); // lqc2 vf10, 448(v1) + // nop // sll r0, r0, 0 + c->lqc2(vf11, 464, v1); // lqc2 vf11, 464(v1) + // nop // sll r0, r0, 0 + c->vftoi4(DEST::xyzw, vf1, vf1); // vftoi4.xyzw vf1, vf1 + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf2, vf2); // vmulq.xyz vf2, vf2, Q + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf6, vf6); // vmulq.xyz vf6, vf6, Q + c->sqc2(vf5, 48, a1); // sqc2 vf5, 48(a1) + // nop // vnop + // nop // sll r0, r0, 0 + // nop // vnop + // nop // sll r0, r0, 0 + c->vdiv(vf25, BC::z, vf3, BC::w); // vdiv Q, vf25.z, vf3.w + c->sqc2(vf9, 64, a1); // sqc2 vf9, 64(a1) + c->lqc2(vf12, 480, v1); // lqc2 vf12, 480(v1) + c->sqc2(vf1, 80, a1); // sqc2 vf1, 80(a1) + c->vadd(DEST::xyzw, vf2, vf2, vf27); // vadd.xyzw vf2, vf2, vf27 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + c->vftoi4(DEST::xyzw, vf2, vf2); // vftoi4.xyzw vf2, vf2 + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf3, vf3); // vmulq.xyz vf3, vf3, Q + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf7, vf7); // vmulq.xyz vf7, vf7, Q + c->sqc2(vf6, 96, a1); // sqc2 vf6, 96(a1) + // nop // vnop + // nop // sll r0, r0, 0 + // nop // vnop + // nop // sll r0, r0, 0 + c->vdiv(vf25, BC::z, vf4, BC::w); // vdiv Q, vf25.z, vf4.w + c->sqc2(vf10, 112, a1); // sqc2 vf10, 112(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf2, 128, a1); // sqc2 vf2, 128(a1) + c->vadd(DEST::xyzw, vf3, vf3, vf27); // vadd.xyzw vf3, vf3, vf27 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + c->vftoi4(DEST::xyzw, vf3, vf3); // vftoi4.xyzw vf3, vf3 + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf4, vf4); // vmulq.xyz vf4, vf4, Q + // nop // sll r0, r0, 0 + c->vmulq(DEST::xyz, vf8, vf8); // vmulq.xyz vf8, vf8, Q + c->sqc2(vf7, 144, a1); // sqc2 vf7, 144(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf11, 160, a1); // sqc2 vf11, 160(a1) + c->vadd(DEST::xyzw, vf4, vf4, vf27); // vadd.xyzw vf4, vf4, vf27 + c->sqc2(vf3, 176, a1); // sqc2 vf3, 176(a1) + c->andi(t4, a0, 2); // andi t4, a0, 2 + // nop // sll r0, r0, 0 + bc = c->sgpr64(t4) == 0; // beq t4, r0, L90 + // nop // sll r0, r0, 0 + if (bc) {goto block_187;} // branch non-likely + + //beq r0, r0, L91 // beq r0, r0, L91 + c->vadd_bc(DEST::x, BC::w, vf23, vf23, vf19); // vaddw.x vf23, vf23, vf19 + goto block_188; // branch always + + + block_187: + // nop // sll r0, r0, 0 + c->vadd_bc(DEST::x, BC::w, vf23, vf23, vf14); // vaddw.x vf23, vf23, vf14 + + block_188: + c->vftoi4(DEST::xyzw, vf4, vf4); // vftoi4.xyzw vf4, vf4 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + // nop // sll r0, r0, 0 + c->sqc2(vf8, 192, a1); // sqc2 vf8, 192(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf12, 208, a1); // sqc2 vf12, 208(a1) + // nop // sll r0, r0, 0 + c->sqc2(vf4, 224, a1); // sqc2 vf4, 224(a1) + //beq r0, r0, L72 // beq r0, r0, L72 + c->daddiu(a1, a1, 240); // daddiu a1, a1, 240 + goto block_132; // branch always + + + block_189: + c->lw(v1, 3024, v1); // lw v1, 3024(v1) + c->sw(a1, 4, v1); // sw a1, 4(v1) + c->lqc2(vf24, 12, a2); // lqc2 vf24, 12(a2) + c->vsub(DEST::xyzw, vf23, vf23, vf24); // vsub.xyzw vf23, vf23, vf24 + c->mov128_gpr_vf(v0, vf23); // qmfc2.i v0, vf23 + //jr ra // jr ra + c->daddu(sp, sp, r0); // daddu sp, sp, r0 + goto end_of_function; // return + + end_of_function: + return c->gprs[v0].du64[0]; +} + +void link() { + cache.font_work = intern_from_c("*font-work*").c(); + cache.font12_table = intern_from_c("*font12-table*").c(); + cache.font24_table = intern_from_c("*font24-table*").c(); + cache.math_camera = intern_from_c("*math-camera*").c(); + cache.video_parms = intern_from_c("*video-parms*").c(); + gLinkedFunctionTable.reg("draw-string", execute, 0); +} + +} // namespace draw_string +} // namespace Mips2C +// clang-format on \ No newline at end of file diff --git a/game/mips2c/functions/test_func.cpp b/game/mips2c/functions/test_func.cpp new file mode 100644 index 0000000000..1ea7492f18 --- /dev/null +++ b/game/mips2c/functions/test_func.cpp @@ -0,0 +1,18 @@ +//--------------------------MIPS2C--------------------- +#include "game/mips2c/mips2c_private.h" +#include "game/kernel/kscheme.h" +namespace Mips2C { +namespace test_func { +u64 execute(void* ctxt) { + auto* c = (ExecutionContext*)ctxt; + u64 result = 0; + + for (int i = a0; i < t4; i++) { + result += c->sgpr64(i); + } + + c->gprs[v0].du64[0] = result; + return 0; +} +} // namespace test_func +} // namespace Mips2C \ No newline at end of file diff --git a/game/mips2c/mips2c_private.h b/game/mips2c/mips2c_private.h new file mode 100644 index 0000000000..5d808d5a6c --- /dev/null +++ b/game/mips2c/mips2c_private.h @@ -0,0 +1,463 @@ +#pragma once + +#include + +#include "common/common_types.h" +#include "game/mips2c/mips2c_table.h" + +// This file contains utility functions for code generated by the mips2c pass. +// This is only useful for + +extern u8* g_ee_main_mem; + +namespace Mips2C { + +// nicknames for GPRs +enum Gpr { + r0 = 0, // hardcoded to zero + at = 1, // temp, not used by GOAL compiler, but used by GOAL's kernel inline assembly (an other + // places?) + v0 = 2, // return, temp + v1 = 3, // temp + a0 = 4, // arg0, temp + a1 = 5, // arg1, temp + a2 = 6, // arg2, temp + a3 = 7, // arg3, temp + t0 = 8, // arg4, temp + t1 = 9, // arg5, temp + t2 = 10, // arg6, temp + t3 = 11, // arg7, temp + t4 = 12, // temp + t5 = 13, // temp + t6 = 14, // temp + t7 = 15, // temp + s0 = 16, // saved + s1 = 17, // saved + s2 = 18, // saved + s3 = 19, // saved + s4 = 20, // saved + s5 = 21, // saved + s6 = 22, // process pointer + s7 = 23, // symbol table + t8 = 24, // temp + t9 = 25, // function pointer + k0 = 26, // reserved + k1 = 27, // reserved + gp = 28, // saved (C code uses this a global pointer) + sp = 29, // stack pointer + fp = 30, // global pointer (address of current function) + ra = 31, // return address + MAX_GPR = 32 +}; + +enum VfName { + vf0 = 0, + vf1 = 1, + vf2 = 2, + vf3 = 3, + vf4 = 4, + vf5 = 5, + vf6 = 6, + vf7 = 7, + vf8 = 8, + vf9 = 9, + vf10 = 10, + vf11 = 11, + vf12 = 12, + vf13 = 13, + vf14 = 14, + vf15 = 15, + vf16 = 16, + vf17 = 17, + vf18 = 18, + vf19 = 19, + vf20 = 20, + vf21 = 21, + vf22 = 22, + vf23 = 23, + vf24 = 24, + vf25 = 25, + vf26 = 26, + vf27 = 27, + vf28 = 28, + vf29 = 29, + vf30 = 30, + vf31 = 31, +}; + +enum FprName { + f0 = 0, + f1 = 1, + f2 = 2, + f3 = 3, + f4 = 4, + f5 = 5, + f6 = 6, + f7 = 7, + f8 = 8, + f9 = 9, + f10 = 10, + f11 = 11, + f12 = 12, + f13 = 13, + f14 = 14, + f15 = 15, + f16 = 16, + f17 = 17, + f18 = 18, + f19 = 19, + f20 = 20, + f21 = 21, + f22 = 22, + f23 = 23, + f24 = 24, + f25 = 25, + f26 = 26, + f27 = 27, + f28 = 28, + f29 = 29, + f30 = 30, + f31 = 31, +}; + +// note: these are not the same as the ps2 encoding - in these the least significant bit is x. +enum class DEST { + NONE = 0, + x = 1, + y = 2, + xy = 3, + z = 4, + xz = 5, + yz = 6, + xyz = 7, + w = 8, + xw = 9, + yw = 10, + xyw = 11, + zw = 12, + xzw = 13, + yzw = 14, + xyzw = 15 +}; + +enum class BC { x = 0, y = 1, z = 2, w = 3 }; + +struct ExecutionContext { + // EE general purpose registers + u128 gprs[32]; + // EE fprs + float fprs[16]; + // VU0 vf registers + u128 vfs[32]; + + u128 acc; + + float Q; + + u128 vf_src(int idx) { + if (idx == 0) { + u128 result; + result.f[0] = 0; + result.f[1] = 0; + result.f[2] = 0; + result.f[3] = 1.f; + return result; + } else { + return vfs[idx]; + } + } + + u128 gpr_src(int idx) { + if (idx == 0) { + u128 result; + result.du64[0] = 0; + result.du64[1] = 0; + return result; + } else { + return gprs[idx]; + } + } + + u64 sgpr64(int idx) { return gpr_src(idx).du64[0]; } + + u32 gpr_addr(int idx) { return gpr_src(idx).du32[0]; } + + void load_symbol(int gpr, void* sym_addr) { + s32 val; + memcpy(&val, sym_addr, 4); + gprs[gpr].ds64[0] = val; // sign extend and set + } + + void lbu(int dst, int offset, int src) { + u8 val; + memcpy(&val, g_ee_main_mem + gpr_src(src).du32[0] + offset, 1); + gprs[dst].du64[0] = val; + } + + void lqc2(int vf, int offset, int gpr) { + memcpy(&vfs[vf], g_ee_main_mem + gpr_src(gpr).du32[0] + offset, 16); + } + + void lw(int dst, int offset, int src) { + s32 val; + memcpy(&val, g_ee_main_mem + gpr_src(src).du32[0] + offset, 4); + gprs[dst].ds64[0] = val; + } + + void lwu(int dst, int offset, int src) { + u32 val; + memcpy(&val, g_ee_main_mem + gpr_src(src).du32[0] + offset, 4); + gprs[dst].du64[0] = val; + } + + void lq(int dst, int offset, int src) { + memcpy(&gprs[dst].du64[0], g_ee_main_mem + gpr_addr(src) + offset, 16); + } + + void sw(int src, int offset, int addr) { + auto s = gpr_src(src); + memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &s.du32[0], 4); + } + + void sq(int src, int offset, int addr) { + auto s = gpr_src(src); + memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &s.du32[0], 16); + } + + void sqc2(int src, int offset, int addr) { + auto s = vf_src(src); + memcpy(g_ee_main_mem + gpr_addr(addr) + offset, &s.du32[0], 16); + } + + void vadd_bc(DEST mask, BC bc, int dest, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dest].f[i] = s0.f[i] + s1.f[(int)bc]; + } + } + } + + void vsub_bc(DEST mask, BC bc, int dest, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dest].f[i] = s0.f[i] - s1.f[(int)bc]; + } + } + } + + void vmul_bc(DEST mask, BC bc, int dest, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dest].f[i] = s0.f[i] * s1.f[(int)bc]; + } + } + } + + void vmul(DEST mask, int dest, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dest].f[i] = s0.f[i] * s1.f[i]; + } + } + } + + void vadd(DEST mask, int dest, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dest].f[i] = s0.f[i] + s1.f[i]; + } + } + } + + void vsub(DEST mask, int dest, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dest].f[i] = s0.f[i] - s1.f[i]; + } + } + } + + void vmula_bc(DEST mask, BC bc, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + acc.f[i] = s0.f[i] * s1.f[(int)bc]; + } + } + } + + void vmadda_bc(DEST mask, BC bc, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + acc.f[i] += s0.f[i] * s1.f[(int)bc]; + } + } + } + + void vmadd_bc(DEST mask, BC bc, int dst, int src0, int src1) { + auto s0 = vf_src(src0); + auto s1 = vf_src(src1); + + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dst].f[i] = acc.f[i] + s0.f[i] * s1.f[(int)bc]; + } + } + } + + void vdiv(int src0, BC bc0, int src1, BC bc1) { + Q = vf_src(src0).f[(int)bc0] / vf_src(src1).f[(int)bc1]; + } + + void vmulq(DEST mask, int dst, int src) { + auto s0 = vf_src(src); + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dst].f[i] = s0.f[i] * Q; + } + } + } + + void mov64(int dest, int src) { gprs[dest].ds64[0] = gpr_src(src).du64[0]; } + + void vmove(DEST mask, int dest, int src) { + auto s = vf_src(src); + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dest].f[i] = s.f[i]; + } + } + } + + void sll(int dst, int src, int sa) { + u32 value = gpr_src(src).du32[0] << sa; + s32 value_signed = value; + gprs[dst].ds64[0] = value_signed; + } + + void sra(int dst, int src, int sa) { gprs[dst].ds64[0] = gpr_src(src).ds32[0] >> sa; } + + void daddu(int dst, int src0, int src1) { gprs[dst].du64[0] = sgpr64(src0) + sgpr64(src1); } + void daddiu(int dst, int src0, s64 imm) { gprs[dst].du64[0] = sgpr64(src0) + imm; } + void addiu(int dst, int src0, s64 imm) { + s32 temp = sgpr64(src0) + imm; + gprs[dst].ds64[0] = temp; + } + + void addu(int dst, int src0, int src1) { + s32 temp = sgpr64(src0) + sgpr64(src1); + gprs[dst].ds64[0] = temp; + } + + void movz(int dst, int src0, int src1) { + if (sgpr64(src1) == 0) { + gprs[dst].du64[0] = sgpr64(src0); + } + } + + void movn(int dst, int src0, int src1) { + if (sgpr64(src1) != 0) { + gprs[dst].du64[0] = sgpr64(src0); + } + } + + void andi(int dest, int src, u64 imm) { gprs[dest].du64[0] = gpr_src(src).du64[0] & imm; } + void ori(int dest, int src, u64 imm) { gprs[dest].du64[0] = gpr_src(src).du64[0] | imm; } + void and_(int dest, int src0, int src1) { + gprs[dest].du64[0] = gpr_src(src0).du64[0] & gpr_src(src1).du64[0]; + } + + void pextlb(int dst, int src0, int src1) { + auto s0 = gpr_src(src0); + auto s1 = gpr_src(src1); + gprs[dst].du8[0] = s1.du8[0]; + gprs[dst].du8[1] = s0.du8[0]; + gprs[dst].du8[2] = s1.du8[1]; + gprs[dst].du8[3] = s0.du8[1]; + gprs[dst].du8[4] = s1.du8[2]; + gprs[dst].du8[5] = s0.du8[2]; + gprs[dst].du8[6] = s1.du8[3]; + gprs[dst].du8[7] = s0.du8[3]; + gprs[dst].du8[8] = s1.du8[4]; + gprs[dst].du8[9] = s0.du8[4]; + gprs[dst].du8[10] = s1.du8[5]; + gprs[dst].du8[11] = s0.du8[5]; + gprs[dst].du8[12] = s1.du8[6]; + gprs[dst].du8[13] = s0.du8[6]; + gprs[dst].du8[14] = s1.du8[7]; + gprs[dst].du8[15] = s0.du8[7]; + } + + void pextlh(int dst, int src0, int src1) { + auto s0 = gpr_src(src0); + auto s1 = gpr_src(src1); + gprs[dst].du16[0] = s1.du16[0]; + gprs[dst].du16[1] = s0.du16[0]; + gprs[dst].du16[2] = s1.du16[1]; + gprs[dst].du16[3] = s0.du16[1]; + gprs[dst].du16[4] = s1.du16[2]; + gprs[dst].du16[5] = s0.du16[2]; + gprs[dst].du16[6] = s1.du16[3]; + gprs[dst].du16[7] = s0.du16[3]; + } + + void por(int dst, int src0, int src1) { + auto s0 = gpr_src(src0); + auto s1 = gpr_src(src1); + gprs[dst].du64[0] = s0.du64[0] | s1.du64[0]; + gprs[dst].du64[1] = s0.du64[1] | s1.du64[1]; + } + + void mov128_vf_gpr(int dst, int src) { vfs[dst] = gpr_src(src); } + void mov128_gpr_vf(int dst, int src) { gprs[dst] = vf_src(src); } + void mov128_gpr_gpr(int dst, int src) { gprs[dst] = gpr_src(src); } + + void vitof0(DEST mask, int dst, int src) { + auto s = vf_src(src); + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dst].f[i] = s.ds32[i]; + } + } + } + + void vftoi4(DEST mask, int dst, int src) { + auto s = vf_src(src); + for (int i = 0; i < 4; i++) { + if ((u64)mask & (1 << i)) { + vfs[dst].ds32[i] = s.f[i] * 16.f; + } + } + } + + void mfc1(int dst, int src) { + s32 val; + memcpy(&val, &fprs[src], 4); + gprs[dst].ds64[0] = val; + } +}; + +} // namespace Mips2C \ No newline at end of file diff --git a/game/mips2c/mips2c_table.cpp b/game/mips2c/mips2c_table.cpp new file mode 100644 index 0000000000..b31fa283f6 --- /dev/null +++ b/game/mips2c/mips2c_table.cpp @@ -0,0 +1,91 @@ +#include "mips2c_table.h" +#include "common/log/log.h" + +#include "game/kernel/kmalloc.h" +#include "game/kernel/kscheme.h" +#include "common/symbols.h" + +extern "C" { +void _mips2c_call_linux(); +void _mips2c_call_windows(); +} + +namespace Mips2C { + +namespace draw_string { +extern void link(); +} + +LinkedFunctionTable gLinkedFunctionTable; +std::unordered_map> gMips2CLinkCallbacks = { + {"font", {draw_string::link}}}; + +void LinkedFunctionTable::reg(const std::string& name, u64 (*exec)(void*), u32 stack_size) { + const auto& it = m_executes.insert({name, {exec, Ptr()}}); + if (!it.second) { + lg::error("MIPS2C Function {} is registered multiple times, ignoring later registrations.", + name); + return; + } + + // this is short stub that will jump to the appropriate function. + auto jump_to_asm = Ptr(alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP, + *(s7 + FIX_SYM_FUNCTION_TYPE), 0x40, UNKNOWN_PP)); + it.first->second.goal_trampoline = jump_to_asm; + + u8* ptr = jump_to_asm.c(); + + { + // linux + + // push the function + u64 addr = (u64)exec; + *ptr = 0x48; + ptr++; + *ptr = 0xb8; + ptr++; + memcpy(ptr, &addr, 8); + ptr += 8; + *ptr = 0x50; + ptr++; + + // push the stack size + addr = stack_size; + *ptr = 0x48; + ptr++; + *ptr = 0xb8; + ptr++; + memcpy(ptr, &addr, 8); + ptr += 8; + *ptr = 0x50; + ptr++; + + // call the other function +#ifdef __linux__ + addr = (u64)_mips2c_call_linux; +#elif _WIN32 + addr = (u64)_mips2c_call_windows; +#endif + + *ptr = 0x48; + ptr++; + *ptr = 0xb8; + ptr++; + memcpy(ptr, &addr, 8); + ptr += 8; + + // jumps to the mips2c call, which will return to the caller of this stub. + *ptr = 0xff; + ptr++; + *ptr = 0xe0; + } +} + +u32 LinkedFunctionTable::get(const std::string& name) { + auto it = m_executes.find(name); + if (it == m_executes.end()) { + assert(false); + } + return it->second.goal_trampoline.offset; +} +} // namespace Mips2C \ No newline at end of file diff --git a/game/mips2c/mips2c_table.h b/game/mips2c/mips2c_table.h new file mode 100644 index 0000000000..b8fca0169d --- /dev/null +++ b/game/mips2c/mips2c_table.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +#include "game/kernel/Ptr.h" +#include "common/common_types.h" +#include "common/util/assert.h" + +namespace Mips2C { + +class LinkedFunctionTable { + public: + void reg(const std::string& name, u64 (*exec)(void*), u32 goal_stack_size); + u32 get(const std::string& name); + + private: + struct Func { + u64 (*c_func)(void*); + Ptr goal_trampoline; + }; + std::unordered_map m_executes; +}; + +extern std::unordered_map> gMips2CLinkCallbacks; +extern LinkedFunctionTable gLinkedFunctionTable; + +} // namespace Mips2C \ No newline at end of file diff --git a/game/mips2c/readme.md b/game/mips2c/readme.md new file mode 100644 index 0000000000..d2498523b5 --- /dev/null +++ b/game/mips2c/readme.md @@ -0,0 +1,89 @@ +## Using Mips2C + +The Mips2C convert very literally translates MIPS assembly to C. Each op is converted to a function call: +```cpp + c->load_symbol(v1, cache.math_camera); // lw v1, *math-camera*(s7) + c->lqc2(vf26, 732, v1); // lqc2 vf26, 732(v1) + c->lqc2(vf27, 732, v1); // lqc2 vf27, 732(v1) + c->vadd_bc(DEST::xy, BC::w, vf26, vf26, vf0); // vaddw.xy vf26, vf26, vf0 + c->vadd_bc(DEST::x, BC::w, vf26, vf26, vf0); // vaddw.x vf26, vf26, vf0 + c->lw(v1, 72, a2); // lw v1, 72(a2) +``` +This is roughly the same thing that the PCSX2 recompiler would do. However, if compiler optimizations are turned on, Mips2C code can be very efficient, as the compiler is often smart enough to avoid loading/storing consecutive uses of a MIPS register. In `draw-string`, clang with `-O3` is about 2x faster than OpenGOAL. + +It also handles branches and delay slots by using `goto`: +```cpp + bc = c->sgpr64(a3) != 0; // bne a3, r0, L22 + c->load_symbol(a3, cache.font12_table); // lw a3, *font12-table*(s7) + if (bc) { + goto block_2; + } // branch non-likely +``` + +Currently supported features are: +- All of the instructions used in `draw-string` +- Some of the vector float ops, including use of accumulator and Q +- Some of the 128-bit integer ops +- Use of the stack, but you must know the maximum stack size used by the function (not its children) +- Use of symbols +- Returning a value + + +The Mips2C converter is intended for complicated assembly functions. Compared to the decompiler, it is much more likely to "just work". It is currently the best option for functions which: +- Have huge amounts assembly branching, making register to variable a huge mess +- Rely on strange details of 128-bit GPR behavior that is not easy to express in OpenGOAL +- Are not understood +- Fails CFG, or other decompiler passes + +To use it, add the function's name to the `mips2c` list in `hacks.jsonc`. + +Not all instructions are implemented yet, but it is generally very easy to add them. It should be possible to use the stack, but this is untested. You must provide the stack size manually. + +There are some limitations: +- Calling GOAL functions is not yet implemented. It is possible but tricky. +- There is no support for static data yet. +- Likely branches are not yet implemented, but should be easy. +- The output is very hard to understand +- 128-bit arguments and return values are not supported yet + +## Mips2C code linking +At link-time, the mips2c code will cache symbol lookups. It will create a `Cache` structure that contains all the symbols used by the function instead of actually patching the code. To set this up, you must call the `link()` function that is autogenerated. There is a system to make this easy: you register a callback that the linker will call when linking the appropriate file. + +First, at the bottom of the mips2 output there will be something like: +```cpp +// FWD DEC: +namespace draw_string { extern void link(); } +``` +this must be copy-pasted into the top of `mips2c_table.cpp` (and can be removed from the `.cpp` file if desired) + +Second, add a new entry to the `gMips2CLinkCallbacks` table in that same file: +```cpp +{"font", {draw_string::link}} +``` +the first thing in the list is the name of the source file that should contain the function (without `.gc`), and the second thing should have the same name as the `namespace` added before. + +When the linker links the `font` object file, it will call the `link` function defined there. This will add the function to the table of available mips2c functions. Note: this does **not** define the function. + +Note: you will need to add a third argument to `gLinkedFunctionTable.reg(` in the auto-generated code with the maximum amount of stack space that the function can use (not including functions it calls, just local use). + +## Accessing the m2c from GOAL + +Replace the `defun` with: +``` +(define my-func (the (function ) (__pc-get-mips2c "draw-string"))) +``` + +You can use the same idea for methods with `method-set!`. The method name will be the decompiler name. + +## Running Mips2C code +When Mips2C code is linked (for the first time), a small dynamically generated function object is created. This is a very short stub that jumps to a common implementation in `mips2c_call_linux`, that actually sets up the call. + +The setup code saves the appropriate registers for the OS, allocates an `ExecutionContext` on the stack, initializes the argument registers, allocates a "fake stack array" on the stack with the requested size, and calls the C++ function. + +The arguments can then be accessed through the register array. The `sp` register is set to point to the "fake stack" on the stack. In this way, it is possible to call other GOAL functions or `suspend` and all the stack stuff will just work. + +Unfortunately, throwing all the registers on the stack takes a huge amount of stack space. It will be somewhere between 1 and 2 kB. For one or two functions this is probably fine, but `suspend`s inside the mips2c will probably fail, for example. + +With some clever tricks it might be possible to do better, but it doesn't seem worth it at this time. + +On exit, the assembly function will grab the return value from `v0` and put it in `rax`. \ No newline at end of file diff --git a/game/runtime.h b/game/runtime.h index ce55081ec9..3296379720 100644 --- a/game/runtime.h +++ b/game/runtime.h @@ -5,9 +5,6 @@ * Setup and launcher for the runtime. */ -#ifndef JAK1_RUNTIME_H -#define JAK1_RUNTIME_H - #include "common/common_types.h" #include @@ -15,5 +12,3 @@ extern u8* g_ee_main_mem; u32 exec_runtime(int argc, char** argv); extern std::thread::id g_main_thread_id; - -#endif // JAK1_RUNTIME_H diff --git a/goal_src/engine/gfx/font.gc b/goal_src/engine/gfx/font.gc index d51350f2f2..6941181a84 100644 --- a/goal_src/engine/gfx/font.gc +++ b/goal_src/engine/gfx/font.gc @@ -612,7 +612,11 @@ ) ) +;; we have both GOAL and mips2c versions of this function +(define draw-string (the (function string dma-buffer font-context float) (__pc-get-mips2c "draw-string"))) + +;; for now, use the GOAL one. the mips2c one is super slow with the game compiled in debug mode. (defun draw-string ((str-in string) (context dma-buffer) (arg2 font-context)) "Draw a string. Writes dma to the given buffer." (local-vars @@ -1614,6 +1618,7 @@ ) + (defun draw-string-adv ((str string) (buf dma-buffer) (ctxt font-context)) "Draw a string and advance the position of the context." (+! (-> ctxt origin x) (draw-string str buf ctxt)) diff --git a/goal_src/engine/math/vector-h.gc b/goal_src/engine/math/vector-h.gc index 3f9a00d5d6..1905568fba 100644 --- a/goal_src/engine/math/vector-h.gc +++ b/goal_src/engine/math/vector-h.gc @@ -574,6 +574,14 @@ ) ) +(defmacro print-vf-dec (vf) + "Print out a vf register as 4x 32-bit base-10 integers" + `(let ((temp (new 'stack 'vector4w))) + (.svf temp ,vf) + (format #t " ~d ~d ~d ~d~%" (-> temp data 0) (-> temp data 1) (-> temp data 2) (-> temp data 3)) + ) + ) + (defun vector4-dot-vu ((a vector) (b vector)) "Take the dot product of two vectors. Does the x, y, z, and w compoments diff --git a/goal_src/kernel-defs.gc b/goal_src/kernel-defs.gc index 45eddae88a..b8ce0181a9 100644 --- a/goal_src/kernel-defs.gc +++ b/goal_src/kernel-defs.gc @@ -308,6 +308,7 @@ (define-extern __send-gfx-dma-chain (function object object none)) (define-extern __pc-texture-upload-now (function object object none)) (define-extern __pc-texture-relocate (function object object object none)) +(define-extern __pc-get-mips2c (function string function)) (define-extern pc-pad-input-mode-set (function symbol none)) (define-extern pc-pad-input-mode-get (function int)) (define-extern pc-pad-input-key-get (function int)) diff --git a/test/goalc/source_templates/with_game/test-mips2c-call.gc b/test/goalc/source_templates/with_game/test-mips2c-call.gc new file mode 100644 index 0000000000..5702e62400 --- /dev/null +++ b/test/goalc/source_templates/with_game/test-mips2c-call.gc @@ -0,0 +1,5 @@ + + +(let ((f (the (function int int int int int int int int int) (__pc-get-mips2c "test-func")))) + (format #t "~D~%" (f 1 2 3 4 5 6 7 8)) + ) \ No newline at end of file diff --git a/test/goalc/test_with_game.cpp b/test/goalc/test_with_game.cpp index d0c0a80b4b..20ac95e14c 100644 --- a/test/goalc/test_with_game.cpp +++ b/test/goalc/test_with_game.cpp @@ -5,6 +5,7 @@ #include "game/runtime.h" #include "goalc/listener/Listener.h" #include "goalc/compiler/Compiler.h" +#include "game/mips2c/mips2c_table.h" #include "inja.hpp" #include "third-party/json.hpp" @@ -895,6 +896,17 @@ TEST_F(WithGameTests, MethodCallForwardDeclared) { {"4 12\n0\n"}); } +namespace Mips2C { +namespace test_func { +extern u64 execute(void*); +} +} // namespace Mips2C + +TEST_F(WithGameTests, Mips2C) { + Mips2C::gLinkedFunctionTable.reg("test-func", Mips2C::test_func::execute, 0); + shared_compiler->runner.run_static_test(env, testCategory, "test-mips2c-call.gc", {"36\n0\n"}); +} + TEST(TypeConsistency, TypeConsistency) { Compiler compiler; compiler.enable_throw_on_redefines(); diff --git a/test/offline/offline_test_main.cpp b/test/offline/offline_test_main.cpp index 65d347b42e..d81eda78b1 100644 --- a/test/offline/offline_test_main.cpp +++ b/test/offline/offline_test_main.cpp @@ -235,7 +235,7 @@ int main(int argc, char** argv) { // Check to see if we've included atleast one of the DGO/CGOs in our hardcoded list // If not BLOW UP bool dgoValidated = false; - for (int i = 0; i < dgoList.size(); i++) { + for (int i = 0; i < (int)dgoList.size(); i++) { std::string& dgo = dgoList.at(i); // can either be in the DGO or CGO folder, and can either end with .CGO or .DGO if (std::find(dgos.begin(), dgos.end(), fmt::format("DGO/{}.DGO", dgo)) != dgos.end() || @@ -383,10 +383,9 @@ TEST_F(OfflineDecompilation, AsmFunction) { int failed_count = 0; db->for_each_function([&](decompiler::Function& func, int, decompiler::ObjectFileData&) { if (func.suspected_asm) { - if (g_functions_expected_to_reject.find(func.guessed_name.to_string()) == + if (g_functions_expected_to_reject.find(func.name()) == g_functions_expected_to_reject.end()) { - lg::error("Function {} was marked as asm, but wasn't expected.", - func.guessed_name.to_string()); + lg::error("Function {} was marked as asm, but wasn't expected.", func.name()); failed_count++; } } @@ -402,7 +401,7 @@ TEST_F(OfflineDecompilation, CfgBuild) { db->for_each_function([&](decompiler::Function& func, int, decompiler::ObjectFileData&) { if (!func.suspected_asm) { if (!func.cfg || !func.cfg->is_fully_resolved()) { - lg::error("Function {} failed cfg", func.guessed_name.to_string()); + lg::error("Function {} failed cfg", func.name()); failed_count++; } } @@ -419,7 +418,7 @@ TEST_F(OfflineDecompilation, AtomicOp) { db->for_each_function([&](decompiler::Function& func, int, decompiler::ObjectFileData&) { if (!func.suspected_asm) { if (!func.ir2.atomic_ops || !func.ir2.atomic_ops_succeeded) { - lg::error("Function {} failed atomic ops", func.guessed_name.to_string()); + lg::error("Function {} failed atomic ops", func.name()); failed_count++; } } @@ -436,7 +435,7 @@ TEST_F(OfflineDecompilation, TypeAnalysis) { db->for_each_function([&](decompiler::Function& func, int, decompiler::ObjectFileData&) { if (!func.suspected_asm) { if (!func.ir2.env.has_type_analysis() || !func.ir2.env.types_succeeded) { - lg::error("Function {} failed types", func.guessed_name.to_string()); + lg::error("Function {} failed types", func.name()); failed_count++; } } @@ -450,7 +449,7 @@ TEST_F(OfflineDecompilation, RegisterUse) { db->for_each_function([&](decompiler::Function& func, int, decompiler::ObjectFileData&) { if (!func.suspected_asm) { if (!func.ir2.env.has_reg_use()) { - lg::error("Function {} failed reg use", func.guessed_name.to_string()); + lg::error("Function {} failed reg use", func.name()); failed_count++; } } @@ -464,7 +463,7 @@ TEST_F(OfflineDecompilation, VariableSSA) { db->for_each_function([&](decompiler::Function& func, int, decompiler::ObjectFileData&) { if (!func.suspected_asm) { if (!func.ir2.env.has_local_vars()) { - lg::error("Function {} failed ssa", func.guessed_name.to_string()); + lg::error("Function {} failed ssa", func.name()); failed_count++; } } @@ -478,7 +477,7 @@ TEST_F(OfflineDecompilation, Structuring) { db->for_each_function([&](decompiler::Function& func, int, decompiler::ObjectFileData&) { if (!func.suspected_asm) { if (!func.ir2.top_form) { - lg::error("Function {} failed structuring", func.guessed_name.to_string()); + lg::error("Function {} failed structuring", func.name()); failed_count++; } } @@ -492,7 +491,7 @@ TEST_F(OfflineDecompilation, Expressions) { db->for_each_function([&](decompiler::Function& func, int, decompiler::ObjectFileData&) { if (!func.suspected_asm) { if (!func.ir2.expressions_succeeded) { - lg::error("Function {} failed expressions", func.guessed_name.to_string()); + lg::error("Function {} failed expressions", func.name()); failed_count++; } } @@ -525,7 +524,7 @@ TEST_F(OfflineDecompilation, Reference) { bool can_cache = true; for (auto& func_list : obj_l.at(0).linked_data.functions_by_seg) { for (auto& func : func_list) { - if (g_functions_to_skip_compiling.find(func.guessed_name.to_string()) != + if (g_functions_to_skip_compiling.find(func.name()) != g_functions_to_skip_compiling.end()) { can_cache = false; break; diff --git a/tools/MemoryDumpTool/main.cpp b/tools/MemoryDumpTool/main.cpp index d501b4ba47..ba518dfb3c 100644 --- a/tools/MemoryDumpTool/main.cpp +++ b/tools/MemoryDumpTool/main.cpp @@ -464,7 +464,7 @@ void inspect_basics(const Ram& ram, int field_addr = base_addr + field.offset(); if (ram.word_in_memory(field_addr)) { auto proc_pointer = ram.word(field_addr); // pointer process - auto pid = ram.word(field_addr + 4); + // auto pid = ram.word(field_addr + 4); if (ram.word_in_memory(proc_pointer)) { auto proc = ram.word(proc_pointer); auto proc_type_tag_addr = proc - 4;