mirror of
https://github.com/open-goal/jak-project
synced 2026-08-03 00:47:12 -04:00
Merge remote-tracking branch 'upstream/master' into logging
This commit is contained in:
+18
-11
@@ -9,11 +9,7 @@
|
||||
"name": "Run Tests - Summary",
|
||||
"args": [
|
||||
"--gtest_brief=1"
|
||||
],
|
||||
"env": {
|
||||
"NEXT_DIR": "${projectDir}",
|
||||
"FAKE_ISO_PATH": "/game/fake_iso.txt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
@@ -22,17 +18,28 @@
|
||||
"name": "Run Tests - Verbose",
|
||||
"args": [
|
||||
"--gtest_brief=0"
|
||||
],
|
||||
"env": {
|
||||
"NEXT_DIR": "${projectDir}",
|
||||
"FAKE_ISO_PATH": "/game/fake_iso.txt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "gk.exe (bin\\gk.exe)",
|
||||
"name": "Run Game"
|
||||
"name": "Run Runtime (no kernel)",
|
||||
"args": [
|
||||
"-fakeiso",
|
||||
"-debug",
|
||||
"-nokernel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "gk.exe (bin\\gk.exe)",
|
||||
"name": "Run Runtime (with kernel)",
|
||||
"args": [
|
||||
"-fakeiso",
|
||||
"-debug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "default",
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Directory of this script
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
$DIR/build/game/gk -fakeiso -debug
|
||||
@@ -1,2 +1,2 @@
|
||||
add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp)
|
||||
target_link_libraries(goos common_util)
|
||||
target_link_libraries(goos common_util fmt)
|
||||
+30
-30
@@ -38,36 +38,36 @@ Interpreter::Interpreter() {
|
||||
{"while", &Interpreter::eval_while},
|
||||
};
|
||||
|
||||
builtin_forms = {
|
||||
{"top-level", &Interpreter::eval_begin},
|
||||
{"begin", &Interpreter::eval_begin},
|
||||
{"exit", &Interpreter::eval_exit},
|
||||
{"read", &Interpreter::eval_read},
|
||||
{"read-file", &Interpreter::eval_read_file},
|
||||
{"print", &Interpreter::eval_print},
|
||||
{"inspect", &Interpreter::eval_inspect},
|
||||
{"load-file", &Interpreter::eval_load_file},
|
||||
{"eq?", &Interpreter::eval_equals},
|
||||
{"gensym", &Interpreter::eval_gensym},
|
||||
{"eval", &Interpreter::eval_eval},
|
||||
{"cons", &Interpreter::eval_cons},
|
||||
{"car", &Interpreter::eval_car},
|
||||
{"cdr", &Interpreter::eval_cdr},
|
||||
{"set-car!", &Interpreter::eval_set_car},
|
||||
{"set-cdr!", &Interpreter::eval_set_cdr},
|
||||
{"+", &Interpreter::eval_plus},
|
||||
{"-", &Interpreter::eval_minus},
|
||||
{"*", &Interpreter::eval_times},
|
||||
{"/", &Interpreter::eval_divide},
|
||||
{"=", &Interpreter::eval_numequals},
|
||||
{"<", &Interpreter::eval_lt},
|
||||
{">", &Interpreter::eval_gt},
|
||||
{"<=", &Interpreter::eval_leq},
|
||||
{">=", &Interpreter::eval_geq},
|
||||
{"null?", &Interpreter::eval_null},
|
||||
{"type?", &Interpreter::eval_type},
|
||||
{"current-method-type", &Interpreter::eval_current_method_type},
|
||||
};
|
||||
builtin_forms = {{"top-level", &Interpreter::eval_begin},
|
||||
{"begin", &Interpreter::eval_begin},
|
||||
{"exit", &Interpreter::eval_exit},
|
||||
{"read", &Interpreter::eval_read},
|
||||
{"read-file", &Interpreter::eval_read_file},
|
||||
{"print", &Interpreter::eval_print},
|
||||
{"inspect", &Interpreter::eval_inspect},
|
||||
{"load-file", &Interpreter::eval_load_file},
|
||||
{"eq?", &Interpreter::eval_equals},
|
||||
{"gensym", &Interpreter::eval_gensym},
|
||||
{"eval", &Interpreter::eval_eval},
|
||||
{"cons", &Interpreter::eval_cons},
|
||||
{"car", &Interpreter::eval_car},
|
||||
{"cdr", &Interpreter::eval_cdr},
|
||||
{"set-car!", &Interpreter::eval_set_car},
|
||||
{"set-cdr!", &Interpreter::eval_set_cdr},
|
||||
{"+", &Interpreter::eval_plus},
|
||||
{"-", &Interpreter::eval_minus},
|
||||
{"*", &Interpreter::eval_times},
|
||||
{"/", &Interpreter::eval_divide},
|
||||
{"=", &Interpreter::eval_numequals},
|
||||
{"<", &Interpreter::eval_lt},
|
||||
{">", &Interpreter::eval_gt},
|
||||
{"<=", &Interpreter::eval_leq},
|
||||
{">=", &Interpreter::eval_geq},
|
||||
{"null?", &Interpreter::eval_null},
|
||||
{"type?", &Interpreter::eval_type},
|
||||
{"current-method-type", &Interpreter::eval_current_method_type},
|
||||
{"fmt", &Interpreter::eval_format},
|
||||
{"error", &Interpreter::eval_error}};
|
||||
|
||||
string_to_type = {{"empty-list", ObjectType::EMPTY_LIST},
|
||||
{"integer", ObjectType::INTEGER},
|
||||
|
||||
@@ -180,6 +180,12 @@ class Interpreter {
|
||||
Object eval_current_method_type(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
Object eval_format(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
Object eval_error(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
|
||||
// specials
|
||||
Object eval_define(const Object& form,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Implementation of built-in GOOS functions.
|
||||
*/
|
||||
|
||||
#include <third-party/fmt/format.h>
|
||||
#include "Interpreter.h"
|
||||
|
||||
namespace goos {
|
||||
@@ -586,4 +587,54 @@ Object Interpreter::eval_current_method_type(const Object& form,
|
||||
vararg_check(form, args, {}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable, goal_to_goos.enclosing_method_type);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_format(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
if (args.unnamed.size() < 2) {
|
||||
throw_eval_error(form, "format must get at least two arguments");
|
||||
}
|
||||
|
||||
auto dest = args.unnamed.at(0);
|
||||
auto format_str = args.unnamed.at(1);
|
||||
if (!format_str.is_string()) {
|
||||
throw_eval_error(form, "format string must be a string");
|
||||
}
|
||||
|
||||
// Note: this might be relying on internal implementation details of libfmt to work properly
|
||||
// and isn't a great solution.
|
||||
std::vector<fmt::basic_format_arg<fmt::format_context>> args2;
|
||||
std::vector<std::string> strings;
|
||||
for (size_t i = 2; i < args.unnamed.size(); i++) {
|
||||
if (args.unnamed.at(i).is_string()) {
|
||||
strings.push_back(args.unnamed.at(i).as_string()->data);
|
||||
} else {
|
||||
strings.push_back(args.unnamed.at(i).print());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& x : strings) {
|
||||
args2.push_back(fmt::detail::make_arg<fmt::format_context>(x));
|
||||
}
|
||||
|
||||
auto formatted =
|
||||
fmt::vformat(format_str.as_string()->data,
|
||||
fmt::format_args(args2.data(), static_cast<unsigned>(args2.size())));
|
||||
|
||||
if (truthy(dest)) {
|
||||
printf("%s", formatted.c_str());
|
||||
}
|
||||
|
||||
return StringObject::make_new(formatted);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_error(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
throw_eval_error(form, "Error: " + args.unnamed.at(0).as_string()->data);
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
} // namespace goos
|
||||
@@ -9,6 +9,7 @@ TypeSystem::TypeSystem() {
|
||||
// the "none" and "_type_" types are included by default.
|
||||
add_type("none", std::make_unique<NullType>("none"));
|
||||
add_type("_type_", std::make_unique<NullType>("_type_"));
|
||||
add_type("_varargs_", std::make_unique<NullType>("_varargs_"));
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -41,7 +42,7 @@ Type* TypeSystem::add_type(const std::string& name, std::unique_ptr<Type> type)
|
||||
// newly defined!
|
||||
|
||||
// none/object get to skip these checks because they are roots.
|
||||
if (name != "object" && name != "none" && name != "_type_") {
|
||||
if (name != "object" && name != "none" && name != "_type_" && name != "_varargs_") {
|
||||
if (m_forward_declared_types.find(type->get_parent()) != m_forward_declared_types.end()) {
|
||||
fmt::print("[TypeSystem] Type {} has incompletely defined parent {}\n", type->get_name(),
|
||||
type->get_parent());
|
||||
@@ -155,6 +156,10 @@ TypeSpec TypeSystem::make_typespec(const std::string& name) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool TypeSystem::fully_defined_type_exists(const std::string& name) const {
|
||||
return m_types.find(name) != m_types.end();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a typespec for a function. If the function doesn't return anything, use "none" as the
|
||||
* return type.
|
||||
@@ -230,8 +235,9 @@ Type* TypeSystem::lookup_type(const TypeSpec& ts) const {
|
||||
|
||||
MethodInfo TypeSystem::add_method(const std::string& type_name,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts) {
|
||||
return add_method(lookup_type(make_typespec(type_name)), method_name, ts);
|
||||
const TypeSpec& ts,
|
||||
bool allow_new_method) {
|
||||
return add_method(lookup_type(make_typespec(type_name)), method_name, ts, allow_new_method);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -243,7 +249,10 @@ MethodInfo TypeSystem::add_method(const std::string& type_name,
|
||||
* is overriding the "new" method - the TypeSystem will track that because overridden new methods
|
||||
* may have different arguments.
|
||||
*/
|
||||
MethodInfo TypeSystem::add_method(Type* type, const std::string& method_name, const TypeSpec& ts) {
|
||||
MethodInfo TypeSystem::add_method(Type* type,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts,
|
||||
bool allow_new_method) {
|
||||
if (method_name == "new") {
|
||||
return add_new_method(type, ts);
|
||||
}
|
||||
@@ -281,6 +290,11 @@ MethodInfo TypeSystem::add_method(Type* type, const std::string& method_name, co
|
||||
|
||||
return existing_info;
|
||||
} else {
|
||||
if (!allow_new_method) {
|
||||
fmt::print("[TypeSystem] Attempted to add method {} to type {} but it was not declared.\n",
|
||||
method_name, type->get_name());
|
||||
throw std::runtime_error("illegal method definition");
|
||||
}
|
||||
// add a new method!
|
||||
return type->add_method({get_next_method_id(type), method_name, ts, type->get_name()});
|
||||
}
|
||||
@@ -295,7 +309,7 @@ MethodInfo TypeSystem::add_new_method(Type* type, const TypeSpec& ts) {
|
||||
MethodInfo existing;
|
||||
if (type->get_my_new_method(&existing)) {
|
||||
// it exists!
|
||||
if (existing.type != ts) {
|
||||
if (!existing.type.is_compatible_child_method(ts, type->get_name())) {
|
||||
fmt::print(
|
||||
"[TypeSystem] The new method of {} was originally defined as {}, but has been redefined "
|
||||
"as {}\n",
|
||||
@@ -483,7 +497,7 @@ int TypeSystem::add_field_to_type(StructureType* type,
|
||||
// we need to compute the offset ourself!
|
||||
offset = align(type->get_size_in_memory(), field_alignment);
|
||||
} else {
|
||||
int aligned_offset = align(type->get_size_in_memory(), field_alignment);
|
||||
int aligned_offset = align(offset, field_alignment);
|
||||
if (offset != aligned_offset) {
|
||||
fmt::print(
|
||||
"[TypeSystem] Tried to overwrite offset of field to be {}, but it is not aligned "
|
||||
@@ -563,13 +577,13 @@ void TypeSystem::add_builtin_types() {
|
||||
// Methods and Fields
|
||||
|
||||
// OBJECT
|
||||
add_method(obj_type, "new", make_function_typespec({"symbol", "type", "int32"}, "_type_"));
|
||||
add_method(obj_type, "new", make_function_typespec({"symbol", "type", "int"}, "_type_"));
|
||||
add_method(obj_type, "delete", make_function_typespec({"_type_"}, "none"));
|
||||
add_method(obj_type, "print", make_function_typespec({"_type_"}, "_type_"));
|
||||
add_method(obj_type, "inspect", make_function_typespec({"_type_"}, "_type_"));
|
||||
add_method(obj_type, "length",
|
||||
make_function_typespec({"_type_"}, "int32")); // todo - this integer type?
|
||||
add_method(obj_type, "asize-of", make_function_typespec({"_type_"}, "int32"));
|
||||
make_function_typespec({"_type_"}, "int")); // todo - this integer type?
|
||||
add_method(obj_type, "asize-of", make_function_typespec({"_type_"}, "int"));
|
||||
add_method(obj_type, "copy", make_function_typespec({"_type_", "symbol"}, "_type_"));
|
||||
add_method(obj_type, "relocate", make_function_typespec({"_type_", "int32"}, "_type_"));
|
||||
add_method(obj_type, "mem-usage",
|
||||
@@ -580,7 +594,7 @@ void TypeSystem::add_builtin_types() {
|
||||
// the type. Dynamic structures use new-dynamic-structure, which is used exactly once ever.
|
||||
add_method(structure_type, "new", make_function_typespec({"symbol", "type"}, "structure"));
|
||||
// structure_type is a field-less StructureType, so we have to do this to match the runtime.
|
||||
structure_type->override_size_in_memory(4);
|
||||
// structure_type->override_size_in_memory(4);
|
||||
|
||||
// BASIC
|
||||
// we intentionally don't inherit from structure because structure's size is weird.
|
||||
@@ -600,12 +614,12 @@ void TypeSystem::add_builtin_types() {
|
||||
add_method(type_type, "new", make_function_typespec({"symbol", "type", "int"}, "_type_"));
|
||||
add_field_to_type(type_type, "symbol", make_typespec("symbol"));
|
||||
add_field_to_type(type_type, "parent", make_typespec("type"));
|
||||
add_field_to_type(type_type, "allocated-size", make_typespec("uint16")); // todo, u16 or s16?
|
||||
add_field_to_type(type_type, "size", make_typespec("uint16")); // actually u16
|
||||
add_field_to_type(type_type, "psize",
|
||||
make_typespec("uint16")); // todo, u16 or s16. what really is this?
|
||||
add_field_to_type(type_type, "heap-base", make_typespec("uint16")); // todo
|
||||
add_field_to_type(type_type, "method-count", make_typespec("uint16")); // todo
|
||||
add_field_to_type(type_type, "vtable", make_typespec("function"), false, true);
|
||||
add_field_to_type(type_type, "heap-base", make_typespec("uint16")); // todo
|
||||
add_field_to_type(type_type, "allocated-length", make_typespec("uint16")); // todo
|
||||
add_field_to_type(type_type, "method-table", make_typespec("function"), false, true);
|
||||
|
||||
// STRING
|
||||
builtin_structure_inherit(string_type);
|
||||
@@ -640,6 +654,8 @@ void TypeSystem::add_builtin_types() {
|
||||
|
||||
// pair
|
||||
pair_type->override_offset(2);
|
||||
add_method(pair_type, "new",
|
||||
make_function_typespec({"symbol", "type", "object", "object"}, "_type_"));
|
||||
add_field_to_type(pair_type, "car", make_typespec("object"));
|
||||
add_field_to_type(pair_type, "cdr", make_typespec("object"));
|
||||
|
||||
@@ -970,4 +986,20 @@ TypeSpec TypeSystem::lowest_common_ancestor(const std::vector<TypeSpec>& types)
|
||||
result = lowest_common_ancestor(result, types.at(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TypeSpec coerce_to_reg_type(const TypeSpec& in) {
|
||||
if (in.arg_count() == 0) {
|
||||
if (in.base_type() == "int8" || in.base_type() == "int16" || in.base_type() == "int32" ||
|
||||
in.base_type() == "int16") {
|
||||
return TypeSpec("int");
|
||||
}
|
||||
|
||||
if (in.base_type() == "uint8" || in.base_type() == "uint16" || in.base_type() == "uint32" ||
|
||||
in.base_type() == "uint16") {
|
||||
return TypeSpec("uint");
|
||||
}
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
@@ -39,6 +39,7 @@ class TypeSystem {
|
||||
|
||||
DerefInfo get_deref_info(const TypeSpec& ts);
|
||||
|
||||
bool fully_defined_type_exists(const std::string& name) const;
|
||||
TypeSpec make_typespec(const std::string& name) const;
|
||||
TypeSpec make_function_typespec(const std::vector<std::string>& arg_types,
|
||||
const std::string& return_type);
|
||||
@@ -53,8 +54,12 @@ class TypeSystem {
|
||||
|
||||
MethodInfo add_method(const std::string& type_name,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts);
|
||||
MethodInfo add_method(Type* type, const std::string& method_name, const TypeSpec& ts);
|
||||
const TypeSpec& ts,
|
||||
bool allow_new_method = true);
|
||||
MethodInfo add_method(Type* type,
|
||||
const std::string& method_name,
|
||||
const TypeSpec& ts,
|
||||
bool allow_new_method = true);
|
||||
MethodInfo add_new_method(Type* type, const TypeSpec& ts);
|
||||
MethodInfo lookup_method(const std::string& type_name, const std::string& method_name);
|
||||
MethodInfo lookup_new_method(const std::string& type_name);
|
||||
@@ -122,4 +127,6 @@ class TypeSystem {
|
||||
bool m_allow_redefinition = false;
|
||||
};
|
||||
|
||||
TypeSpec coerce_to_reg_type(const TypeSpec& in);
|
||||
|
||||
#endif // JAK_TYPESYSTEM_H
|
||||
|
||||
@@ -78,24 +78,6 @@ int64_t get_int(const goos::Object& obj) {
|
||||
throw std::runtime_error(obj.print() + " was supposed to be an integer, but isn't");
|
||||
}
|
||||
|
||||
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src) {
|
||||
if (src.is_symbol()) {
|
||||
return type_system->make_typespec(symbol_string(src));
|
||||
} else if (src.is_pair()) {
|
||||
TypeSpec ts = type_system->make_typespec(symbol_string(car(&src)));
|
||||
const auto& rest = *cdr(&src);
|
||||
|
||||
for_each_in_list(rest,
|
||||
[&](const goos::Object& o) { ts.add_arg(parse_typespec(type_system, o)); });
|
||||
|
||||
return ts;
|
||||
} else {
|
||||
throw std::runtime_error("invalid typespec: " + src.print());
|
||||
}
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
void add_field(StructureType* structure, TypeSystem* ts, const goos::Object& def) {
|
||||
auto rest = &def;
|
||||
|
||||
@@ -278,6 +260,24 @@ TypeFlags parse_structure_def(StructureType* type,
|
||||
|
||||
} // namespace
|
||||
|
||||
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src) {
|
||||
if (src.is_symbol()) {
|
||||
return type_system->make_typespec(symbol_string(src));
|
||||
} else if (src.is_pair()) {
|
||||
TypeSpec ts = type_system->make_typespec(symbol_string(car(&src)));
|
||||
const auto& rest = *cdr(&src);
|
||||
|
||||
for_each_in_list(rest,
|
||||
[&](const goos::Object& o) { ts.add_arg(parse_typespec(type_system, o)); });
|
||||
|
||||
return ts;
|
||||
} else {
|
||||
throw std::runtime_error("invalid typespec: " + src.print());
|
||||
}
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts) {
|
||||
auto iter = &deftype;
|
||||
|
||||
|
||||
@@ -22,3 +22,4 @@ struct DeftypeResult {
|
||||
};
|
||||
|
||||
DeftypeResult parse_deftype(const goos::Object& deftype, TypeSystem* ts);
|
||||
TypeSpec parse_typespec(TypeSystem* type_system, const goos::Object& src);
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@
|
||||
|
||||
namespace versions {
|
||||
// language version
|
||||
constexpr s32 GOAL_VERSION_MAJOR = 2;
|
||||
constexpr s32 GOAL_VERSION_MINOR = 6;
|
||||
constexpr s32 GOAL_VERSION_MAJOR = 0;
|
||||
constexpr s32 GOAL_VERSION_MINOR = 1;
|
||||
} // namespace versions
|
||||
|
||||
// GOAL kernel version
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# Directory of this script
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
$DIR/build/decompiler/decompiler $DIR/decompiler/config/jak1_ntsc_black_label.jsonc $DIR/iso_data $DIR/decompiler_out
|
||||
$DIR/build/decompiler/decompiler $DIR/decompiler/config/jak1_ntsc_black_label.jsonc $DIR/iso_data $DIR/decompiler_out
|
||||
|
||||
@@ -12,14 +12,15 @@ add_executable(decompiler
|
||||
util/FileIO.cpp
|
||||
config.cpp
|
||||
util/LispPrint.cpp
|
||||
util/DecompilerTypeSystem.cpp
|
||||
Function/BasicBlocks.cpp
|
||||
Disasm/InstructionMatching.cpp
|
||||
TypeSystem/GoalType.cpp
|
||||
TypeSystem/GoalFunction.cpp
|
||||
TypeSystem/GoalSymbol.cpp
|
||||
TypeSystem/TypeInfo.cpp
|
||||
TypeSystem/TypeSpec.cpp Function/CfgVtx.cpp Function/CfgVtx.h)
|
||||
Function/CfgVtx.cpp Function/CfgVtx.h
|
||||
IR/BasicOpBuilder.cpp
|
||||
IR/IR.cpp)
|
||||
|
||||
target_link_libraries(decompiler
|
||||
minilzo
|
||||
common_util)
|
||||
common_util
|
||||
type_system
|
||||
fmt)
|
||||
@@ -44,6 +44,12 @@ struct InstructionAtom {
|
||||
std::string to_string(const LinkedObjectFile& file) const;
|
||||
|
||||
bool is_link_or_label() const;
|
||||
bool is_reg() const { return kind == REGISTER; }
|
||||
bool is_imm() const { return kind == IMM; }
|
||||
bool is_label() const { return kind == LABEL; }
|
||||
bool is_sym() const { return kind == IMM_SYM; }
|
||||
|
||||
bool is_reg(Register r) const { return kind == REGISTER && reg == r; }
|
||||
|
||||
private:
|
||||
int32_t imm;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* @file InstructionMatching.h
|
||||
* Utilities for checking if an instruction matches some criteria.
|
||||
*/
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H
|
||||
#define JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*!
|
||||
* @file OpcodeInfo.cpp
|
||||
* Decoding info for each opcode.
|
||||
*/
|
||||
|
||||
#include "OpcodeInfo.h"
|
||||
#include <cassert>
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_BASICBLOCKS_H
|
||||
#define JAK_DISASSEMBLER_BASICBLOCKS_H
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
@@ -21,5 +18,3 @@ struct BasicBlock {
|
||||
std::vector<BasicBlock> find_blocks_in_function(const LinkedObjectFile& file,
|
||||
int seg,
|
||||
const Function& func);
|
||||
|
||||
#endif // JAK_DISASSEMBLER_BASICBLOCKS_H
|
||||
|
||||
@@ -1637,7 +1637,6 @@ void ControlFlowGraph::flag_early_exit(const std::vector<BasicBlock>& blocks) {
|
||||
* Build and resolve a Control Flow Graph as much as possible.
|
||||
*/
|
||||
std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int seg, Function& func) {
|
||||
printf("build cfg : %s\n", func.guessed_name.to_string().c_str());
|
||||
auto cfg = std::make_shared<ControlFlowGraph>();
|
||||
|
||||
const auto& blocks = cfg->create_blocks(func.basic_blocks.size());
|
||||
@@ -1716,13 +1715,6 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int se
|
||||
|
||||
cfg->flag_early_exit(func.basic_blocks);
|
||||
|
||||
// if(func.guessed_name.to_string() == "(method 9 thread)")
|
||||
// cfg->find_cond_w_else();
|
||||
|
||||
// if (func.guessed_name.to_string() != "looping-code") {
|
||||
// return cfg;
|
||||
// }
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
@@ -1730,11 +1722,9 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int se
|
||||
// printf("%s\n", cfg->to_dot().c_str());
|
||||
// printf("%s\n", cfg->to_form()->toStringPretty().c_str());
|
||||
|
||||
changed = changed | cfg->find_cond_w_else();
|
||||
changed = changed | cfg->find_cond_n_else();
|
||||
changed = changed || cfg->find_cond_w_else();
|
||||
changed = changed || cfg->find_cond_n_else();
|
||||
changed = changed || cfg->find_while_loop_top_level();
|
||||
// //// printf("while loops? %d\n", changed);
|
||||
//// changed = changed || cfg->find_if_else_top_level();
|
||||
changed = changed || cfg->find_seq_top_level();
|
||||
changed = changed || cfg->find_short_circuits();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "Function.h"
|
||||
#include "decompiler/Disasm/InstructionMatching.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "decompiler/TypeSystem/TypeInfo.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
|
||||
namespace {
|
||||
std::vector<Register> gpr_backups = {make_gpr(Reg::GP), make_gpr(Reg::S5), make_gpr(Reg::S4),
|
||||
@@ -418,7 +418,7 @@ void Function::check_epilogue(const LinkedObjectFile& file) {
|
||||
*
|
||||
* Updates the guessed_name of the function and updates type_info
|
||||
*/
|
||||
void Function::find_global_function_defs(LinkedObjectFile& file) {
|
||||
void Function::find_global_function_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts) {
|
||||
int state = 0;
|
||||
int label_id = -1;
|
||||
Register reg;
|
||||
@@ -457,7 +457,8 @@ void Function::find_global_function_defs(LinkedObjectFile& file) {
|
||||
auto& func = file.get_function_at_label(label_id);
|
||||
assert(func.guessed_name.empty());
|
||||
func.guessed_name.set_as_global(name);
|
||||
get_type_info().inform_symbol(name, TypeSpec("function"));
|
||||
dts.add_symbol(name, "function");
|
||||
;
|
||||
// todo - inform function.
|
||||
}
|
||||
|
||||
@@ -549,4 +550,42 @@ void Function::find_method_defs(LinkedObjectFile& file) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Function::add_basic_op(std::shared_ptr<IR> op, int start_instr, int end_instr) {
|
||||
op->is_basic_op = true;
|
||||
assert(end_instr > start_instr);
|
||||
|
||||
for (int i = start_instr; i < end_instr; i++) {
|
||||
instruction_to_basic_op[i] = basic_ops.size();
|
||||
}
|
||||
basic_op_to_instruction[basic_ops.size()] = start_instr;
|
||||
basic_ops.push_back(op);
|
||||
}
|
||||
|
||||
bool Function::instr_starts_basic_op(int idx) {
|
||||
auto op = instruction_to_basic_op.find(idx);
|
||||
if (op != instruction_to_basic_op.end()) {
|
||||
auto start_instr = basic_op_to_instruction.at(op->second);
|
||||
return start_instr == idx;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IR* Function::get_basic_op_at_instr(int idx) {
|
||||
return basic_ops.at(instruction_to_basic_op.at(idx)).get();
|
||||
}
|
||||
|
||||
int Function::get_basic_op_count() {
|
||||
return basic_ops.size();
|
||||
}
|
||||
|
||||
int Function::get_failed_basic_op_count() {
|
||||
int count = 0;
|
||||
for (auto& x : basic_ops) {
|
||||
if (dynamic_cast<IR_Failed*>(x.get())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -8,6 +8,9 @@
|
||||
#include "decompiler/Disasm/Instruction.h"
|
||||
#include "BasicBlocks.h"
|
||||
#include "CfgVtx.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
|
||||
class DecompilerTypeSystem;
|
||||
|
||||
struct FunctionName {
|
||||
enum class FunctionKind {
|
||||
@@ -60,8 +63,14 @@ class Function {
|
||||
public:
|
||||
Function(int _start_word, int _end_word);
|
||||
void analyze_prologue(const LinkedObjectFile& file);
|
||||
void find_global_function_defs(LinkedObjectFile& file);
|
||||
void find_global_function_defs(LinkedObjectFile& file, DecompilerTypeSystem& dts);
|
||||
void find_method_defs(LinkedObjectFile& file);
|
||||
void add_basic_op(std::shared_ptr<IR> op, int start_instr, int end_instr);
|
||||
bool has_basic_ops() { return !basic_ops.empty(); }
|
||||
bool instr_starts_basic_op(int idx);
|
||||
IR* get_basic_op_at_instr(int idx);
|
||||
int get_basic_op_count();
|
||||
int get_failed_basic_op_count();
|
||||
|
||||
int segment = -1;
|
||||
int start_word = -1;
|
||||
@@ -115,6 +124,9 @@ class Function {
|
||||
|
||||
private:
|
||||
void check_epilogue(const LinkedObjectFile& file);
|
||||
std::vector<std::shared_ptr<IR>> basic_ops;
|
||||
std::unordered_map<int, int> instruction_to_basic_op;
|
||||
std::unordered_map<int, int> basic_op_to_instruction;
|
||||
};
|
||||
|
||||
#endif // NEXT_FUNCTION_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
/*!
|
||||
* @file BasicOpBuilder.h
|
||||
* Analyzes a basic block and converts instructions to BasicOps.
|
||||
* These will be used later to convert the Cfg into the nested IR format.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class Function;
|
||||
struct BasicBlock;
|
||||
class LinkedObjectFile;
|
||||
|
||||
void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjectFile* file);
|
||||
@@ -0,0 +1,391 @@
|
||||
#include "IR.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
|
||||
std::string IR::print(const LinkedObjectFile& file) const {
|
||||
return to_form(file)->toStringPretty();
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Register::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(reg.to_charp());
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Set::to_form(const LinkedObjectFile& file) const {
|
||||
return buildList(toForm("set!"), dst->to_form(file), src->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Store::to_form(const LinkedObjectFile& file) const {
|
||||
std::string store_operator;
|
||||
switch (kind) {
|
||||
case FLOAT:
|
||||
store_operator = "s.f";
|
||||
break;
|
||||
case INTEGER:
|
||||
switch (size) {
|
||||
case 1:
|
||||
store_operator = "s.b";
|
||||
break;
|
||||
case 2:
|
||||
store_operator = "s.h";
|
||||
break;
|
||||
case 4:
|
||||
store_operator = "s.w";
|
||||
break;
|
||||
case 8:
|
||||
store_operator = "s.d";
|
||||
break;
|
||||
case 16:
|
||||
store_operator = "s.q";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return buildList(toForm(store_operator), dst->to_form(file), src->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Failed::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("INVALID-OPERATION");
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Symbol::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm("'" + name);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_SymbolValue::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(name);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_StaticAddress::to_form(const LinkedObjectFile& file) const {
|
||||
// return buildList(toForm("&"), file.get_label_name(label_id));
|
||||
return toForm(file.get_label_name(label_id));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Load::to_form(const LinkedObjectFile& file) const {
|
||||
std::string load_operator;
|
||||
switch (kind) {
|
||||
case FLOAT:
|
||||
load_operator = "l.f";
|
||||
break;
|
||||
case UNSIGNED:
|
||||
switch (size) {
|
||||
case 1:
|
||||
load_operator = "l.bu";
|
||||
break;
|
||||
case 2:
|
||||
load_operator = "l.hu";
|
||||
break;
|
||||
case 4:
|
||||
load_operator = "l.wu";
|
||||
break;
|
||||
case 8:
|
||||
load_operator = "l.d";
|
||||
break;
|
||||
case 16:
|
||||
load_operator = "l.q";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
case SIGNED:
|
||||
switch (size) {
|
||||
case 1:
|
||||
load_operator = "l.bs";
|
||||
break;
|
||||
case 2:
|
||||
load_operator = "l.hs";
|
||||
break;
|
||||
case 4:
|
||||
load_operator = "l.ws";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(load_operator), location->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_FloatMath2::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case DIV:
|
||||
math_operator = "/.f";
|
||||
break;
|
||||
case MUL:
|
||||
math_operator = "*.f";
|
||||
break;
|
||||
case ADD:
|
||||
math_operator = "+.f";
|
||||
break;
|
||||
case SUB:
|
||||
math_operator = "-.f";
|
||||
break;
|
||||
case MIN:
|
||||
math_operator = "min.f";
|
||||
break;
|
||||
case MAX:
|
||||
math_operator = "max.f";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return buildList(toForm(math_operator), arg0->to_form(file), arg1->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntMath2::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case ADD:
|
||||
math_operator = "+.i";
|
||||
break;
|
||||
case SUB:
|
||||
math_operator = "-.i";
|
||||
break;
|
||||
case MUL_SIGNED:
|
||||
math_operator = "*.si";
|
||||
break;
|
||||
case MUL_UNSIGNED:
|
||||
math_operator = "*.ui";
|
||||
break;
|
||||
case DIV_SIGNED:
|
||||
math_operator = "/.si";
|
||||
break;
|
||||
case MOD_SIGNED:
|
||||
math_operator = "mod.si";
|
||||
break;
|
||||
case DIV_UNSIGNED:
|
||||
math_operator = "/.ui";
|
||||
break;
|
||||
case MOD_UNSIGNED:
|
||||
math_operator = "mod.ui";
|
||||
break;
|
||||
case OR:
|
||||
math_operator = "logior";
|
||||
break;
|
||||
case AND:
|
||||
math_operator = "logand";
|
||||
break;
|
||||
case NOR:
|
||||
math_operator = "lognor";
|
||||
break;
|
||||
case XOR:
|
||||
math_operator = "logxor";
|
||||
break;
|
||||
case LEFT_SHIFT:
|
||||
math_operator = "shl";
|
||||
break;
|
||||
case RIGHT_SHIFT_ARITH:
|
||||
math_operator = "sar";
|
||||
break;
|
||||
case RIGHT_SHIFT_LOGIC:
|
||||
math_operator = "shr";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg0->to_form(file), arg1->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntMath1::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case NOT:
|
||||
math_operator = "lognot";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_FloatMath1::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case FLOAT_TO_INT:
|
||||
math_operator = "int<-float";
|
||||
break;
|
||||
case INT_TO_FLOAT:
|
||||
math_operator = "float<-int";
|
||||
break;
|
||||
case ABS:
|
||||
math_operator = "abs.f";
|
||||
break;
|
||||
case NEG:
|
||||
math_operator = "neg.f";
|
||||
break;
|
||||
case SQRT:
|
||||
math_operator = "sqrt.f";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return buildList(toForm(math_operator), arg->to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Call::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("call!");
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_IntegerConstant::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return toForm(std::to_string(value));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> BranchDelay::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
switch (kind) {
|
||||
case NOP:
|
||||
return buildList("nop");
|
||||
case SET_REG_FALSE:
|
||||
return buildList(toForm("set!"), destination->to_form(file), "'#f");
|
||||
case SET_REG_TRUE:
|
||||
return buildList(toForm("set!"), destination->to_form(file), "'#t");
|
||||
case SET_REG_REG:
|
||||
return buildList(toForm("set!"), destination->to_form(file), source->to_form(file));
|
||||
case UNKNOWN:
|
||||
return buildList("unknown-branch-delay");
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Nop::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("nop!");
|
||||
}
|
||||
|
||||
int Condition::num_args() const {
|
||||
switch (kind) {
|
||||
case NOT_EQUAL:
|
||||
case EQUAL:
|
||||
case LESS_THAN_SIGNED:
|
||||
case LESS_THAN_UNSIGNED:
|
||||
case GREATER_THAN_SIGNED:
|
||||
case GREATER_THAN_UNSIGNED:
|
||||
case LEQ_SIGNED:
|
||||
case GEQ_SIGNED:
|
||||
case LEQ_UNSIGNED:
|
||||
case GEQ_UNSIGNED:
|
||||
case FLOAT_EQUAL:
|
||||
case FLOAT_NOT_EQUAL:
|
||||
case FLOAT_LESS_THAN:
|
||||
case FLOAT_GEQ:
|
||||
return 2;
|
||||
case ZERO:
|
||||
case NONZERO:
|
||||
case FALSE:
|
||||
case TRUTHY:
|
||||
return 1;
|
||||
case ALWAYS:
|
||||
return 0;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> Condition::to_form(const LinkedObjectFile& file) const {
|
||||
int nargs = num_args();
|
||||
std::string condtion_operator;
|
||||
switch (kind) {
|
||||
case NOT_EQUAL:
|
||||
condtion_operator = "!=";
|
||||
break;
|
||||
case EQUAL:
|
||||
condtion_operator = "=";
|
||||
break;
|
||||
case LESS_THAN_SIGNED:
|
||||
condtion_operator = "<.si";
|
||||
break;
|
||||
case LESS_THAN_UNSIGNED:
|
||||
condtion_operator = "<.ui";
|
||||
break;
|
||||
case GREATER_THAN_SIGNED:
|
||||
condtion_operator = ">.si";
|
||||
break;
|
||||
case GREATER_THAN_UNSIGNED:
|
||||
condtion_operator = ">.ui";
|
||||
break;
|
||||
case LEQ_SIGNED:
|
||||
condtion_operator = "<=.si";
|
||||
break;
|
||||
case GEQ_SIGNED:
|
||||
condtion_operator = ">=.si";
|
||||
break;
|
||||
case LEQ_UNSIGNED:
|
||||
condtion_operator = "<=.ui";
|
||||
break;
|
||||
case GEQ_UNSIGNED:
|
||||
condtion_operator = ">=.ui";
|
||||
break;
|
||||
case ZERO:
|
||||
condtion_operator = "zero?";
|
||||
break;
|
||||
case NONZERO:
|
||||
condtion_operator = "nonzero?";
|
||||
break;
|
||||
case FALSE:
|
||||
condtion_operator = "not";
|
||||
break;
|
||||
case TRUTHY:
|
||||
condtion_operator = "";
|
||||
break;
|
||||
case ALWAYS:
|
||||
condtion_operator = "'#t";
|
||||
break;
|
||||
case FLOAT_EQUAL:
|
||||
condtion_operator = "=.f";
|
||||
break;
|
||||
case FLOAT_NOT_EQUAL:
|
||||
condtion_operator = "!=.f";
|
||||
break;
|
||||
case FLOAT_LESS_THAN:
|
||||
condtion_operator = "<.f";
|
||||
break;
|
||||
case FLOAT_GEQ:
|
||||
condtion_operator = ">=.f";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (nargs == 2) {
|
||||
return buildList(toForm(condtion_operator), src0->to_form(file), src1->to_form(file));
|
||||
} else if (nargs == 1) {
|
||||
if (condtion_operator.empty()) {
|
||||
return src0->to_form(file);
|
||||
} else {
|
||||
return buildList(toForm(condtion_operator), src0->to_form(file));
|
||||
}
|
||||
} else if (nargs == 0) {
|
||||
return toForm(condtion_operator);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Branch::to_form(const LinkedObjectFile& file) const {
|
||||
return buildList(toForm(likely ? "bl!" : "b!"), condition.to_form(file),
|
||||
toForm(file.get_label_name(dest_label_idx)), branch_delay.to_form(file));
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Compare::to_form(const LinkedObjectFile& file) const {
|
||||
return condition.to_form(file);
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> IR_Suspend::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return buildList("suspend!");
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
#ifndef JAK_IR_H
|
||||
#define JAK_IR_H
|
||||
|
||||
#include <cassert>
|
||||
#include "decompiler/Disasm/Register.h"
|
||||
#include "decompiler/util/LispPrint.h"
|
||||
|
||||
class LinkedObjectFile;
|
||||
|
||||
class IR {
|
||||
public:
|
||||
virtual std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const = 0;
|
||||
std::string print(const LinkedObjectFile& file) const;
|
||||
|
||||
bool is_basic_op = false;
|
||||
};
|
||||
|
||||
class IR_Failed : public IR {
|
||||
public:
|
||||
IR_Failed() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Register : public IR {
|
||||
public:
|
||||
IR_Register(Register _reg, int _instr_idx) : reg(_reg), instr_idx(_instr_idx) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
Register reg;
|
||||
int instr_idx = -1;
|
||||
};
|
||||
|
||||
class IR_Set : public IR {
|
||||
public:
|
||||
enum Kind {
|
||||
REG_64,
|
||||
LOAD,
|
||||
STORE,
|
||||
SYM_LOAD,
|
||||
SYM_STORE,
|
||||
FPR_TO_GPR64,
|
||||
GPR_TO_FPR,
|
||||
REG_FLT,
|
||||
REG_I128
|
||||
} kind;
|
||||
IR_Set(Kind _kind, std::shared_ptr<IR> _dst, std::shared_ptr<IR> _src)
|
||||
: kind(_kind), dst(std::move(_dst)), src(std::move(_src)) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
std::shared_ptr<IR> dst, src;
|
||||
std::shared_ptr<IR> clobber = nullptr;
|
||||
};
|
||||
|
||||
class IR_Store : public IR_Set {
|
||||
public:
|
||||
enum Kind { INTEGER, FLOAT } kind;
|
||||
IR_Store(Kind _kind, std::shared_ptr<IR> _dst, std::shared_ptr<IR> _src, int _size)
|
||||
: IR_Set(IR_Set::LOAD, std::move(_dst), std::move(_src)), kind(_kind), size(_size) {}
|
||||
int size;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Symbol : public IR {
|
||||
public:
|
||||
IR_Symbol(std::string _name) : name(std::move(_name)) {}
|
||||
std::string name;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_SymbolValue : public IR {
|
||||
public:
|
||||
IR_SymbolValue(std::string _name) : name(std::move(_name)) {}
|
||||
std::string name;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_StaticAddress : public IR {
|
||||
public:
|
||||
IR_StaticAddress(int _label_id) : label_id(_label_id) {}
|
||||
int label_id = -1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Load : public IR {
|
||||
public:
|
||||
enum Kind { UNSIGNED, SIGNED, FLOAT } kind;
|
||||
|
||||
IR_Load(Kind _kind, int _size, const std::shared_ptr<IR>& _location)
|
||||
: kind(_kind), size(_size), location(_location) {}
|
||||
int size;
|
||||
std::shared_ptr<IR> location;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_FloatMath2 : public IR {
|
||||
public:
|
||||
enum Kind { DIV, MUL, ADD, SUB, MIN, MAX } kind;
|
||||
IR_FloatMath2(Kind _kind, std::shared_ptr<IR> _arg0, std::shared_ptr<IR> _arg1)
|
||||
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
|
||||
std::shared_ptr<IR> arg0, arg1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_FloatMath1 : public IR {
|
||||
public:
|
||||
enum Kind { FLOAT_TO_INT, INT_TO_FLOAT, ABS, NEG, SQRT } kind;
|
||||
IR_FloatMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
|
||||
std::shared_ptr<IR> arg;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntMath2 : public IR {
|
||||
public:
|
||||
enum Kind {
|
||||
ADD,
|
||||
SUB,
|
||||
MUL_SIGNED,
|
||||
DIV_SIGNED,
|
||||
MOD_SIGNED,
|
||||
DIV_UNSIGNED,
|
||||
MOD_UNSIGNED,
|
||||
OR,
|
||||
AND,
|
||||
NOR,
|
||||
XOR,
|
||||
LEFT_SHIFT,
|
||||
RIGHT_SHIFT_ARITH,
|
||||
RIGHT_SHIFT_LOGIC,
|
||||
MUL_UNSIGNED
|
||||
} kind;
|
||||
IR_IntMath2(Kind _kind, std::shared_ptr<IR> _arg0, std::shared_ptr<IR> _arg1)
|
||||
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
|
||||
std::shared_ptr<IR> arg0, arg1;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntMath1 : public IR {
|
||||
public:
|
||||
enum Kind { NOT } kind;
|
||||
IR_IntMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
|
||||
std::shared_ptr<IR> arg;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Call : public IR {
|
||||
public:
|
||||
IR_Call() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_IntegerConstant : public IR {
|
||||
public:
|
||||
int64_t value;
|
||||
explicit IR_IntegerConstant(int64_t _value) : value(_value) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
struct BranchDelay {
|
||||
enum Kind { NOP, SET_REG_FALSE, SET_REG_TRUE, SET_REG_REG, UNKNOWN } kind;
|
||||
std::shared_ptr<IR> destination = nullptr, source = nullptr;
|
||||
BranchDelay(Kind _kind) : kind(_kind) {}
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const;
|
||||
};
|
||||
|
||||
struct Condition {
|
||||
enum Kind {
|
||||
NOT_EQUAL,
|
||||
EQUAL,
|
||||
LESS_THAN_SIGNED,
|
||||
GREATER_THAN_SIGNED,
|
||||
LEQ_SIGNED,
|
||||
GEQ_SIGNED,
|
||||
LESS_THAN_UNSIGNED,
|
||||
GREATER_THAN_UNSIGNED,
|
||||
LEQ_UNSIGNED,
|
||||
GEQ_UNSIGNED,
|
||||
ZERO,
|
||||
NONZERO,
|
||||
FALSE,
|
||||
TRUTHY,
|
||||
ALWAYS,
|
||||
FLOAT_EQUAL,
|
||||
FLOAT_NOT_EQUAL,
|
||||
FLOAT_LESS_THAN,
|
||||
FLOAT_GEQ
|
||||
} kind;
|
||||
|
||||
Condition(Kind _kind,
|
||||
std::shared_ptr<IR> _src0,
|
||||
std::shared_ptr<IR> _src1,
|
||||
std::shared_ptr<IR> _clobber)
|
||||
: kind(_kind), src0(std::move(_src0)), src1(std::move(_src1)), clobber(std::move(_clobber)) {
|
||||
int nargs = num_args();
|
||||
if (nargs == 2) {
|
||||
assert(src0 && src1);
|
||||
} else if (nargs == 1) {
|
||||
assert(src0 && !src1);
|
||||
} else if (nargs == 0) {
|
||||
assert(!src0 && !src1);
|
||||
}
|
||||
}
|
||||
|
||||
int num_args() const;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const;
|
||||
std::shared_ptr<IR> src0, src1, clobber;
|
||||
};
|
||||
|
||||
class IR_Branch : public IR {
|
||||
public:
|
||||
IR_Branch(Condition _condition, int _dest_label_idx, BranchDelay _branch_delay, bool _likely)
|
||||
: condition(std::move(_condition)),
|
||||
dest_label_idx(_dest_label_idx),
|
||||
branch_delay(std::move(_branch_delay)),
|
||||
likely(_likely) {}
|
||||
|
||||
Condition condition;
|
||||
int dest_label_idx;
|
||||
BranchDelay branch_delay;
|
||||
bool likely;
|
||||
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Compare : public IR {
|
||||
public:
|
||||
explicit IR_Compare(Condition _condition) : condition(std::move(_condition)) {}
|
||||
|
||||
Condition condition;
|
||||
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Nop : public IR {
|
||||
public:
|
||||
IR_Nop() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
class IR_Suspend : public IR {
|
||||
public:
|
||||
IR_Suspend() = default;
|
||||
std::shared_ptr<Form> to_form(const LinkedObjectFile& file) const override;
|
||||
};
|
||||
|
||||
#endif // JAK_IR_H
|
||||
@@ -483,6 +483,8 @@ void LinkedObjectFile::process_fp_relative_links() {
|
||||
if (pprev_instr && pprev_instr->kind == InstructionKind::LUI) {
|
||||
assert(pprev_instr->get_dst(0).get_reg() == offset_reg);
|
||||
additional_offset = (1 << 16) * pprev_instr->get_imm_src().get_imm();
|
||||
pprev_instr->get_imm_src().set_label(
|
||||
get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset));
|
||||
}
|
||||
atom.set_label(
|
||||
get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset));
|
||||
@@ -554,6 +556,12 @@ std::string LinkedObjectFile::print_disassembly() {
|
||||
auto& word = words_by_seg[seg].at(func.start_word + i);
|
||||
append_word_to_string(result, word);
|
||||
} else {
|
||||
if (func.has_basic_ops() && func.instr_starts_basic_op(i)) {
|
||||
if (line.length() < 40) {
|
||||
line.append(40 - line.length(), ' ');
|
||||
}
|
||||
line += ";; " + func.get_basic_op_at_instr(i)->print(*this);
|
||||
}
|
||||
result += line + "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <cstring>
|
||||
#include "LinkedObjectFileCreation.h"
|
||||
#include "decompiler/config.h"
|
||||
#include "decompiler/TypeSystem/TypeInfo.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
|
||||
// There are three link versions:
|
||||
// V2 - not really in use anymore, but V4 will resue logic from it (and the game didn't rename the
|
||||
@@ -86,8 +86,9 @@ static uint32_t c_symlink2(LinkedObjectFile& f,
|
||||
uint32_t link_ptr_offset,
|
||||
SymbolLinkKind kind,
|
||||
const char* name,
|
||||
int seg_id) {
|
||||
get_type_info().inform_symbol_with_no_type_info(name);
|
||||
int seg_id,
|
||||
DecompilerTypeSystem& dts) {
|
||||
dts.add_symbol(name);
|
||||
auto initial_offset = code_ptr_offset;
|
||||
do {
|
||||
auto table_value = data.at(link_ptr_offset);
|
||||
@@ -130,7 +131,7 @@ static uint32_t c_symlink2(LinkedObjectFile& f,
|
||||
word_kind = LinkedWord::EMPTY_PTR;
|
||||
break;
|
||||
case SymbolLinkKind::TYPE:
|
||||
get_type_info().inform_type(name);
|
||||
dts.add_symbol(name, "type");
|
||||
word_kind = LinkedWord::TYPE_PTR;
|
||||
break;
|
||||
default:
|
||||
@@ -162,8 +163,9 @@ static uint32_t c_symlink3(LinkedObjectFile& f,
|
||||
uint32_t link_ptr,
|
||||
SymbolLinkKind kind,
|
||||
const char* name,
|
||||
int seg) {
|
||||
get_type_info().inform_symbol_with_no_type_info(name);
|
||||
int seg,
|
||||
DecompilerTypeSystem& dts) {
|
||||
dts.add_symbol(name);
|
||||
auto initial_offset = code_ptr;
|
||||
do {
|
||||
// seek, with a variable length encoding that sucks.
|
||||
@@ -187,7 +189,7 @@ static uint32_t c_symlink3(LinkedObjectFile& f,
|
||||
word_kind = LinkedWord::EMPTY_PTR;
|
||||
break;
|
||||
case SymbolLinkKind::TYPE:
|
||||
get_type_info().inform_type(name);
|
||||
dts.add_symbol(name, "type");
|
||||
word_kind = LinkedWord::TYPE_PTR;
|
||||
break;
|
||||
default:
|
||||
@@ -223,7 +225,8 @@ static uint32_t align16(uint32_t in) {
|
||||
*/
|
||||
static void link_v4(LinkedObjectFile& f,
|
||||
const std::vector<uint8_t>& data,
|
||||
const std::string& name) {
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts) {
|
||||
// read the V4 header to find where the link data really is
|
||||
const auto* header = (const LinkHeaderV4*)&data.at(0);
|
||||
uint32_t link_data_offset = header->code_size + sizeof(LinkHeaderV4); // no basic offset
|
||||
@@ -358,7 +361,7 @@ static void link_v4(LinkedObjectFile& f,
|
||||
|
||||
link_ptr_offset += strlen(s_name) + 1;
|
||||
f.stats.total_v2_symbol_count++;
|
||||
link_ptr_offset = c_symlink2(f, data, code_offset, link_ptr_offset, kind, s_name, 0);
|
||||
link_ptr_offset = c_symlink2(f, data, code_offset, link_ptr_offset, kind, s_name, 0, dts);
|
||||
if (data.at(link_ptr_offset) == 0)
|
||||
break;
|
||||
}
|
||||
@@ -384,7 +387,8 @@ static void assert_string_empty_after(const char* str, int size) {
|
||||
|
||||
static void link_v5(LinkedObjectFile& f,
|
||||
const std::vector<uint8_t>& data,
|
||||
const std::string& name) {
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts) {
|
||||
auto header = (const LinkHeaderV5*)(&data.at(0));
|
||||
if (header->n_segments == 1) {
|
||||
printf("abandon %s!\n", name.c_str());
|
||||
@@ -539,10 +543,10 @@ static void link_v5(LinkedObjectFile& f,
|
||||
|
||||
if (std::string("_empty_") == sname) {
|
||||
link_ptr = c_symlink2(f, data, segment_data_offsets[seg_id], link_ptr,
|
||||
SymbolLinkKind::EMPTY_LIST, sname, seg_id);
|
||||
SymbolLinkKind::EMPTY_LIST, sname, seg_id, dts);
|
||||
} else {
|
||||
link_ptr = c_symlink2(f, data, segment_data_offsets[seg_id], link_ptr,
|
||||
SymbolLinkKind::SYMBOL, sname, seg_id);
|
||||
SymbolLinkKind::SYMBOL, sname, seg_id, dts);
|
||||
}
|
||||
} else if ((reloc & 0x3f) == 0x3f) {
|
||||
assert(false); // todo, does this ever get hit?
|
||||
@@ -556,7 +560,7 @@ static void link_v5(LinkedObjectFile& f,
|
||||
const char* sname = (const char*)(&data.at(link_ptr));
|
||||
link_ptr += strlen(sname) + 1;
|
||||
link_ptr = c_symlink2(f, data, segment_data_offsets[seg_id], link_ptr,
|
||||
SymbolLinkKind::TYPE, sname, seg_id);
|
||||
SymbolLinkKind::TYPE, sname, seg_id, dts);
|
||||
}
|
||||
|
||||
sub_link_ptr = link_ptr;
|
||||
@@ -586,7 +590,8 @@ static void link_v5(LinkedObjectFile& f,
|
||||
|
||||
static void link_v3(LinkedObjectFile& f,
|
||||
const std::vector<uint8_t>& data,
|
||||
const std::string& name) {
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts) {
|
||||
auto header = (const LinkHeaderV3*)(&data.at(0));
|
||||
assert(name == header->name);
|
||||
assert(header->segments == 3);
|
||||
@@ -739,7 +744,7 @@ static void link_v3(LinkedObjectFile& f,
|
||||
// methods todo
|
||||
|
||||
s_name = (const char*)(&data.at(link_ptr));
|
||||
get_type_info().inform_type_method_count(s_name, reloc & 0x7f);
|
||||
// get_type_info().inform_type_method_count(s_name, reloc & 0x7f); todo
|
||||
kind = SymbolLinkKind::TYPE;
|
||||
}
|
||||
|
||||
@@ -750,7 +755,7 @@ static void link_v3(LinkedObjectFile& f,
|
||||
|
||||
link_ptr += strlen(s_name) + 1;
|
||||
f.stats.v3_symbol_count++;
|
||||
link_ptr = c_symlink3(f, data, base_ptr, link_ptr, kind, s_name, seg_id);
|
||||
link_ptr = c_symlink3(f, data, base_ptr, link_ptr, kind, s_name, seg_id, dts);
|
||||
}
|
||||
segment_link_ends[seg_id] = link_ptr;
|
||||
}
|
||||
@@ -775,19 +780,21 @@ static void link_v3(LinkedObjectFile& f,
|
||||
/*!
|
||||
* Main function to generate LinkedObjectFiles from raw object data.
|
||||
*/
|
||||
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data, const std::string& name) {
|
||||
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data,
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts) {
|
||||
LinkedObjectFile result;
|
||||
const auto* header = (const LinkHeaderCommon*)&data.at(0);
|
||||
|
||||
// use appropriate linker
|
||||
if (header->version == 3) {
|
||||
assert(header->type_tag == 0);
|
||||
link_v3(result, data, name);
|
||||
link_v3(result, data, name, dts);
|
||||
} else if (header->version == 4) {
|
||||
assert(header->type_tag == 0xffffffff);
|
||||
link_v4(result, data, name);
|
||||
link_v4(result, data, name, dts);
|
||||
} else if (header->version == 5) {
|
||||
link_v5(result, data, name);
|
||||
link_v5(result, data, name, dts);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
#include "LinkedObjectFile.h"
|
||||
|
||||
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data, const std::string& name);
|
||||
class DecompilerTypeSystem;
|
||||
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data,
|
||||
const std::string& name,
|
||||
DecompilerTypeSystem& dts);
|
||||
|
||||
#endif // NEXT_LINKEDOBJECTFILECREATION_H
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "common/util/Timer.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "decompiler/Function/BasicBlocks.h"
|
||||
#include "decompiler/IR/BasicOpBuilder.h"
|
||||
|
||||
/*!
|
||||
* Get a unique name for this object file.
|
||||
@@ -64,6 +65,8 @@ ObjectFileData& ObjectFileDB::lookup_record(ObjectFileRecord rec) {
|
||||
*/
|
||||
ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos) {
|
||||
Timer timer;
|
||||
printf("- Loading Types...\n");
|
||||
dts.parse_type_defs({"decompiler", "config", "all-types.gc"});
|
||||
|
||||
printf("- Initializing ObjectFileDB...\n");
|
||||
for (auto& dgo : _dgos) {
|
||||
@@ -71,7 +74,7 @@ ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos) {
|
||||
}
|
||||
|
||||
printf("ObjectFileDB Initialized:\n");
|
||||
printf(" total dgos: %lld\n", _dgos.size());
|
||||
printf(" total dgos: %d\n", int(_dgos.size()));
|
||||
printf(" total data: %d bytes\n", stats.total_dgo_bytes);
|
||||
printf(" total objs: %d\n", stats.total_obj_files);
|
||||
printf(" unique objs: %d\n", stats.unique_obj_files);
|
||||
@@ -356,7 +359,7 @@ void ObjectFileDB::process_link_data() {
|
||||
LinkedObjectFile::Stats combined_stats;
|
||||
|
||||
for_each_obj([&](ObjectFileData& obj) {
|
||||
obj.linked_data = to_linked_object_file(obj.data, obj.record.name);
|
||||
obj.linked_data = to_linked_object_file(obj.data, obj.record.name, dts);
|
||||
combined_stats.add(obj.linked_data.stats);
|
||||
});
|
||||
|
||||
@@ -543,7 +546,7 @@ void ObjectFileDB::analyze_functions() {
|
||||
auto& func = data.linked_data.functions_by_seg.at(2).front();
|
||||
assert(func.guessed_name.empty());
|
||||
func.guessed_name.set_as_top_level();
|
||||
func.find_global_function_defs(data.linked_data);
|
||||
func.find_global_function_defs(data.linked_data, dts);
|
||||
func.find_method_defs(data.linked_data);
|
||||
}
|
||||
});
|
||||
@@ -591,6 +594,8 @@ void ObjectFileDB::analyze_functions() {
|
||||
int total_nontrivial_functions = 0;
|
||||
int total_resolved_nontrivial_functions = 0;
|
||||
int total_named_functions = 0;
|
||||
int total_basic_ops = 0;
|
||||
int total_failed_basic_ops = 0;
|
||||
|
||||
std::map<int, std::vector<std::string>> unresolved_by_length;
|
||||
if (get_config().find_basic_blocks) {
|
||||
@@ -601,10 +606,18 @@ void ObjectFileDB::analyze_functions() {
|
||||
total_basic_blocks += blocks.size();
|
||||
func.basic_blocks = blocks;
|
||||
|
||||
total_functions++;
|
||||
if (!func.suspected_asm) {
|
||||
func.analyze_prologue(data.linked_data);
|
||||
func.cfg = build_cfg(data.linked_data, segment_id, func);
|
||||
total_functions++;
|
||||
for (auto& block : func.basic_blocks) {
|
||||
if (block.end_word > block.start_word) {
|
||||
add_basic_ops_to_block(&func, block, &data.linked_data);
|
||||
}
|
||||
}
|
||||
total_basic_ops += func.get_basic_op_count();
|
||||
total_failed_basic_ops += func.get_failed_basic_op_count();
|
||||
|
||||
if (func.cfg->is_fully_resolved()) {
|
||||
resolved_cfg_functions++;
|
||||
}
|
||||
@@ -640,11 +653,15 @@ void ObjectFileDB::analyze_functions() {
|
||||
total_nontrivial_functions,
|
||||
100.f * float(total_resolved_nontrivial_functions) / float(total_nontrivial_functions));
|
||||
|
||||
for (auto& kv : unresolved_by_length) {
|
||||
printf("LEN %d\n", kv.first);
|
||||
for (auto& x : kv.second) {
|
||||
printf(" %s\n", x.c_str());
|
||||
}
|
||||
}
|
||||
int successful_basic_ops = total_basic_ops - total_failed_basic_ops;
|
||||
printf(" %d/%d basic ops converted successfully (%.2f%%)\n", successful_basic_ops,
|
||||
total_basic_ops, 100.f * float(successful_basic_ops) / float(total_basic_ops));
|
||||
|
||||
// for (auto& kv : unresolved_by_length) {
|
||||
// printf("LEN %d\n", kv.first);
|
||||
// for (auto& x : kv.second) {
|
||||
// printf(" %s\n", x.c_str());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "LinkedObjectFile.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
|
||||
/*!
|
||||
* A "record" which can be used to identify an object file.
|
||||
@@ -55,6 +56,7 @@ class ObjectFileDB {
|
||||
void write_disassembly(const std::string& output_dir, bool disassemble_objects_without_functions);
|
||||
void analyze_functions();
|
||||
ObjectFileData& lookup_record(ObjectFileRecord rec);
|
||||
DecompilerTypeSystem dts;
|
||||
|
||||
private:
|
||||
void get_objs_from_dgo(const std::string& filename);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#include "GoalFunction.h"
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_GOALFUNCTION_H
|
||||
#define JAK_DISASSEMBLER_GOALFUNCTION_H
|
||||
|
||||
class GoalFunction {
|
||||
public:
|
||||
// enum Kind {
|
||||
// GLOBAL_FUNCTION,
|
||||
// ANON_FUNCTION,
|
||||
// METHOD,
|
||||
// BEHAVIOR,
|
||||
// UNKNOWN
|
||||
// };
|
||||
};
|
||||
|
||||
#endif // JAK_DISASSEMBLER_GOALFUNCTION_H
|
||||
@@ -1 +0,0 @@
|
||||
#include "GoalSymbol.h"
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_GOALSYMBOL_H
|
||||
#define JAK_DISASSEMBLER_GOALSYMBOL_H
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include "TypeSpec.h"
|
||||
|
||||
class GoalSymbol {
|
||||
public:
|
||||
GoalSymbol() = default;
|
||||
explicit GoalSymbol(std::string name) : m_name(std::move(name)) {}
|
||||
GoalSymbol(std::string name, TypeSpec ts) : m_name(std::move(name)), m_type(std::move(ts)) {
|
||||
m_has_type_info = true;
|
||||
}
|
||||
|
||||
bool has_type_info() const { return m_has_type_info; }
|
||||
|
||||
void set_type(TypeSpec ts) {
|
||||
if (m_has_type_info) {
|
||||
if (ts != m_type) {
|
||||
printf("symbol %s %s -> %s", m_name.c_str(), m_type.to_string().c_str(),
|
||||
ts.to_string().c_str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
m_has_type_info = true;
|
||||
m_type = std::move(ts);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
TypeSpec m_type;
|
||||
bool m_has_type_info = false;
|
||||
};
|
||||
|
||||
#endif // JAK_DISASSEMBLER_GOALSYMBOL_H
|
||||
@@ -1,13 +0,0 @@
|
||||
#include "GoalType.h"
|
||||
|
||||
void GoalType::set_methods(int n) {
|
||||
if (m_method_count_set) {
|
||||
if (m_method_count != n) {
|
||||
printf("Type %s had %d methods, set_methods tried to change it to %d\n", m_name.c_str(),
|
||||
m_method_count, n);
|
||||
}
|
||||
} else {
|
||||
m_method_count = n;
|
||||
m_method_count_set = true;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_GOALTYPE_H
|
||||
#define JAK_DISASSEMBLER_GOALTYPE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class GoalType {
|
||||
public:
|
||||
GoalType() = default;
|
||||
GoalType(std::string name) : m_name(std::move(name)) {}
|
||||
bool has_info() const { return m_has_info; }
|
||||
|
||||
bool has_method_count() const { return m_method_count_set; }
|
||||
|
||||
void set_methods(int n);
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
bool m_has_info = false;
|
||||
bool m_method_count_set = false;
|
||||
int m_method_count = -1;
|
||||
};
|
||||
|
||||
#endif // JAK_DISASSEMBLER_GOALTYPE_H
|
||||
@@ -1,99 +0,0 @@
|
||||
#include "TypeInfo.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
TypeInfo gTypeInfo;
|
||||
}
|
||||
|
||||
TypeInfo::TypeInfo() {
|
||||
GoalType type_type("type");
|
||||
m_types["type"] = type_type;
|
||||
GoalSymbol type_symbol("type");
|
||||
m_symbols["type"] = type_symbol;
|
||||
}
|
||||
|
||||
TypeInfo& get_type_info() {
|
||||
return gTypeInfo;
|
||||
}
|
||||
|
||||
std::string TypeInfo::get_summary() {
|
||||
int total_symbols = 0;
|
||||
int syms_with_type_info = 0;
|
||||
for (const auto& kv : m_symbols) {
|
||||
total_symbols++;
|
||||
if (kv.second.has_type_info()) {
|
||||
syms_with_type_info++;
|
||||
}
|
||||
}
|
||||
|
||||
int total_types = 0;
|
||||
int types_with_info = 0;
|
||||
int types_with_method_count = 0;
|
||||
for (const auto& kv : m_types) {
|
||||
total_types++;
|
||||
if (kv.second.has_info()) {
|
||||
types_with_info++;
|
||||
}
|
||||
if (kv.second.has_method_count()) {
|
||||
types_with_method_count++;
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
sprintf(buffer,
|
||||
"TypeInfo Summary\n"
|
||||
" Total Symbols: %d\n"
|
||||
" with type info: %d (%.2f%%)\n"
|
||||
" Total Types: %d\n"
|
||||
" with info: %d (%.2f%%)\n"
|
||||
" with method count: %d (%.2f%%)\n",
|
||||
total_symbols, syms_with_type_info,
|
||||
100.f * float(syms_with_type_info) / float(total_symbols), total_types, types_with_info,
|
||||
100.f * float(types_with_info) / float(total_types), types_with_method_count,
|
||||
100.f * float(types_with_method_count) / float(total_types));
|
||||
|
||||
return {buffer};
|
||||
}
|
||||
|
||||
/*!
|
||||
* inform TypeInfo that there is a symbol with this name.
|
||||
* Provides no type info - if some is already known there is no change.
|
||||
*/
|
||||
void TypeInfo::inform_symbol_with_no_type_info(const std::string& name) {
|
||||
if (m_symbols.find(name) == m_symbols.end()) {
|
||||
// only add it if we haven't seen this already.
|
||||
GoalSymbol sym(name);
|
||||
m_symbols[name] = sym;
|
||||
}
|
||||
}
|
||||
|
||||
void TypeInfo::inform_symbol(const std::string& name, TypeSpec type) {
|
||||
inform_symbol_with_no_type_info(name);
|
||||
m_symbols.at(name).set_type(std::move(type));
|
||||
}
|
||||
|
||||
void TypeInfo::inform_type(const std::string& name) {
|
||||
if (m_types.find(name) == m_types.end()) {
|
||||
GoalType typ(name);
|
||||
m_types[name] = typ;
|
||||
}
|
||||
inform_symbol(name, TypeSpec("type"));
|
||||
}
|
||||
|
||||
void TypeInfo::inform_type_method_count(const std::string& name, int methods) {
|
||||
// create type and symbol
|
||||
inform_type(name);
|
||||
m_types.at(name).set_methods(methods);
|
||||
}
|
||||
|
||||
std::string TypeInfo::get_all_symbols_debug() {
|
||||
std::string result = "const char* all_syms[" + std::to_string(m_symbols.size()) + "] = {";
|
||||
for (auto& x : m_symbols) {
|
||||
result += "\"" + x.first + "\",";
|
||||
}
|
||||
if (!result.empty()) {
|
||||
result.pop_back();
|
||||
}
|
||||
return result + "};";
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_TYPEINFO_H
|
||||
#define JAK_DISASSEMBLER_TYPEINFO_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include "GoalType.h"
|
||||
#include "GoalFunction.h"
|
||||
#include "GoalSymbol.h"
|
||||
|
||||
class TypeInfo {
|
||||
public:
|
||||
TypeInfo();
|
||||
|
||||
void inform_symbol(const std::string& name, TypeSpec type);
|
||||
void inform_symbol_with_no_type_info(const std::string& name);
|
||||
void inform_type(const std::string& name);
|
||||
void inform_type_method_count(const std::string& name, int methods);
|
||||
|
||||
std::string get_summary();
|
||||
std::string get_all_symbols_debug();
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, GoalType> m_types;
|
||||
std::unordered_map<std::string, GoalFunction> m_global_functions;
|
||||
std::unordered_map<std::string, GoalSymbol> m_symbols;
|
||||
};
|
||||
|
||||
TypeInfo& get_type_info();
|
||||
void init_type_info();
|
||||
|
||||
#endif // JAK_DISASSEMBLER_TYPEINFO_H
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "TypeSpec.h"
|
||||
|
||||
std::string TypeSpec::to_string() const {
|
||||
if (m_args.empty()) {
|
||||
return m_base_type;
|
||||
} else {
|
||||
std::string result = "(";
|
||||
result += m_base_type;
|
||||
for (const auto& x : m_args) {
|
||||
result += " ";
|
||||
result += x.to_string();
|
||||
}
|
||||
result += ")";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> TypeSpec::to_form() const {
|
||||
if (m_args.empty()) {
|
||||
return toForm(m_base_type);
|
||||
} else {
|
||||
std::vector<std::shared_ptr<Form>> all;
|
||||
all.push_back(toForm(m_base_type));
|
||||
for (const auto& x : m_args) {
|
||||
all.push_back(x.to_form());
|
||||
}
|
||||
return buildList(all);
|
||||
}
|
||||
}
|
||||
|
||||
bool TypeSpec::operator==(const TypeSpec& other) const {
|
||||
if (m_base_type != other.m_base_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_args.size() != other.m_args.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_args.size(); i++) {
|
||||
if (m_args[i] != other.m_args[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TypeSpec::operator!=(const TypeSpec& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_DISASSEMBLER_TYPESPEC_H
|
||||
#define JAK_DISASSEMBLER_TYPESPEC_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "decompiler/util/LispPrint.h"
|
||||
|
||||
class TypeSpec {
|
||||
public:
|
||||
TypeSpec() = default;
|
||||
explicit TypeSpec(std::string base_type) : m_base_type(std::move(base_type)) {}
|
||||
TypeSpec(std::string base_type, std::vector<TypeSpec> args)
|
||||
: m_base_type(std::move(base_type)), m_args(std::move(args)) {}
|
||||
|
||||
std::string to_string() const;
|
||||
std::shared_ptr<Form> to_form() const;
|
||||
|
||||
bool operator==(const TypeSpec& other) const;
|
||||
bool operator!=(const TypeSpec& other) const;
|
||||
|
||||
private:
|
||||
std::string m_base_type;
|
||||
std::vector<TypeSpec> m_args;
|
||||
};
|
||||
|
||||
#endif // JAK_DISASSEMBLER_TYPESPEC_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,13 +3,14 @@
|
||||
{
|
||||
"game_version":1,
|
||||
// the order here matters. KERNEL and GAME should go first
|
||||
"dgo_names":["CGO/KERNEL.CGO"
|
||||
, "CGO/GAME.CGO", "CGO/ENGINE.CGO"
|
||||
"dgo_names":["CGO/KERNEL.CGO", "CGO/GAME.CGO"],
|
||||
/*, "CGO/ENGINE.CGO"
|
||||
, "CGO/ART.CGO", "DGO/BEA.DGO", "DGO/CIT.DGO", "CGO/COMMON.CGO", "DGO/DAR.DGO", "DGO/DEM.DGO",
|
||||
"DGO/FIN.DGO", "DGO/INT.DGO", "DGO/JUB.DGO", "DGO/JUN.DGO", "CGO/JUNGLE.CGO", "CGO/L1.CGO", "DGO/FIC.DGO",
|
||||
"DGO/LAV.DGO", "DGO/MAI.DGO", "CGO/MAINCAVE.CGO", "DGO/MIS.DGO", "DGO/OGR.DGO", "CGO/RACERP.CGO", "DGO/ROB.DGO", "DGO/ROL.DGO",
|
||||
"DGO/SNO.DGO", "DGO/SUB.DGO", "DGO/SUN.DGO", "CGO/SUNKEN.CGO", "DGO/SWA.DGO", "DGO/TIT.DGO", "DGO/TRA.DGO", "DGO/VI1.DGO",
|
||||
"DGO/VI2.DGO", "DGO/VI3.DGO", "CGO/VILLAGEP.CGO", "CGO/WATER-AN.CGO"],
|
||||
"DGO/VI2.DGO", "DGO/VI3.DGO", "CGO/VILLAGEP.CGO", "CGO/WATER-AN.CGO"
|
||||
],*/
|
||||
|
||||
"write_disassembly":true,
|
||||
"write_hex_near_instructions":false,
|
||||
@@ -29,9 +30,16 @@
|
||||
|
||||
"asm_functions_by_name":[
|
||||
// gcommon
|
||||
"ash", "abs", "min", "max", "collide-do-primitives", "draw-bones-check-longest-edge-asm",
|
||||
"ash", "abs", "min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!",
|
||||
|
||||
// pskernel
|
||||
"resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", "return-from-exception",
|
||||
"kernel-read", "kernel-read-function", "kernel-write", "kernel-write-function", "kernel-copy-to-kernel-ram",
|
||||
|
||||
"collide-do-primitives", "draw-bones-check-longest-edge-asm",
|
||||
"sp-launch-particles-var", "(method 15 collide-shape-prim-mesh)", "(method 15 collide-shape-prim-sphere)",
|
||||
"(method 45 collide-shape)", "cam-layout-save-cam-trans", "kernel-copy-function", "dma-sync-hang", "generic-no-light-dproc", "dma-sync-fast", "bsp-camera-asm",
|
||||
"(method 45 collide-shape)", "cam-layout-save-cam-trans", "kernel-copy-function", "dma-sync-hang", "generic-no-light-dproc",
|
||||
"dma-sync-fast", "bsp-camera-asm",
|
||||
"generic-none-dma-wait", "unpack-comp-rle", "level-remap-texture", "(method 10 collide-edge-hold-list)"
|
||||
]
|
||||
}
|
||||
+5
-4
@@ -4,7 +4,7 @@
|
||||
#include "ObjectFile/ObjectFileDB.h"
|
||||
#include "config.h"
|
||||
#include "util/FileIO.h"
|
||||
#include "TypeSystem/TypeInfo.h"
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -48,8 +48,9 @@ int main(int argc, char** argv) {
|
||||
db.write_disassembly(out_folder, get_config().disassemble_objects_without_functions);
|
||||
}
|
||||
|
||||
printf("%s\n", get_type_info().get_summary().c_str());
|
||||
// printf("%d\n", InstructionKind::EE_OP_MAX);
|
||||
// printf("%s\n", get_type_info().get_all_symbols_debug().c_str());
|
||||
// todo print type summary
|
||||
// printf("%s\n", get_type_info().get_summary().c_str());
|
||||
|
||||
file_util::write_text_file(combine_path(out_folder, "all-syms.gc"), db.dts.dump_symbol_types());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "DecompilerTypeSystem.h"
|
||||
#include "common/goos/Reader.h"
|
||||
#include "common/type_system/deftype.h"
|
||||
|
||||
DecompilerTypeSystem::DecompilerTypeSystem() {
|
||||
ts.add_builtin_types();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// some utilities for parsing the type def file
|
||||
|
||||
goos::Object& car(goos::Object& pair) {
|
||||
if (pair.is_pair()) {
|
||||
return pair.as_pair()->car;
|
||||
} else {
|
||||
throw std::runtime_error("car called on something that wasn't a pair: " + pair.print());
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object& cdr(goos::Object& pair) {
|
||||
if (pair.is_pair()) {
|
||||
return pair.as_pair()->cdr;
|
||||
} else {
|
||||
throw std::runtime_error("cdr called on something that wasn't a pair");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void for_each_in_list(goos::Object& list, T f) {
|
||||
goos::Object* iter = &list;
|
||||
while (iter->is_pair()) {
|
||||
f(car(*iter));
|
||||
iter = &cdr(*iter);
|
||||
}
|
||||
|
||||
if (!iter->is_empty_list()) {
|
||||
throw std::runtime_error("malformed list");
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void DecompilerTypeSystem::parse_type_defs(const std::vector<std::string>& file_path) {
|
||||
goos::Reader reader;
|
||||
auto read = reader.read_from_file(file_path);
|
||||
auto data = cdr(read);
|
||||
|
||||
for_each_in_list(data, [&](goos::Object& o) {
|
||||
if (car(o).as_symbol()->name == "define-extern") {
|
||||
auto* rest = &cdr(o);
|
||||
auto sym_name = car(*rest);
|
||||
rest = &cdr(*rest);
|
||||
auto sym_type = car(*rest);
|
||||
if (!cdr(*rest).is_empty_list()) {
|
||||
throw std::runtime_error("malformed define-extern");
|
||||
}
|
||||
add_symbol(sym_name.as_symbol()->name, parse_typespec(&ts, sym_type));
|
||||
|
||||
} else if (car(o).as_symbol()->name == "deftype") {
|
||||
parse_deftype(cdr(o), &ts);
|
||||
} else {
|
||||
throw std::runtime_error("Decompiler cannot parse " + car(o).print());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::string DecompilerTypeSystem::dump_symbol_types() {
|
||||
assert(symbol_add_order.size() == symbols.size());
|
||||
std::string result;
|
||||
for (auto& symbol_name : symbol_add_order) {
|
||||
auto skv = symbol_types.find(symbol_name);
|
||||
if (skv == symbol_types.end()) {
|
||||
result += fmt::format(";;(define-extern {} object) ;; unknown type\n", symbol_name);
|
||||
} else {
|
||||
result += fmt::format("(define-extern {} {})\n", symbol_name, skv->second.print());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef JAK_DECOMPILERTYPESYSTEM_H
|
||||
#define JAK_DECOMPILERTYPESYSTEM_H
|
||||
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
class DecompilerTypeSystem {
|
||||
public:
|
||||
DecompilerTypeSystem();
|
||||
TypeSystem ts;
|
||||
std::unordered_map<std::string, TypeSpec> symbol_types;
|
||||
std::unordered_set<std::string> symbols;
|
||||
std::vector<std::string> symbol_add_order;
|
||||
|
||||
void add_symbol(const std::string& name) {
|
||||
if (symbols.find(name) == symbols.end()) {
|
||||
symbols.insert(name);
|
||||
symbol_add_order.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
void add_symbol(const std::string& name, const std::string& base_type) {
|
||||
add_symbol(name, TypeSpec(base_type));
|
||||
}
|
||||
|
||||
void add_symbol(const std::string& name, const TypeSpec& type_spec) {
|
||||
add_symbol(name);
|
||||
auto skv = symbol_types.find(name);
|
||||
if (skv == symbol_types.end() || skv->second == type_spec) {
|
||||
symbol_types[name] = type_spec;
|
||||
} else {
|
||||
if (ts.typecheck(type_spec, skv->second, "", false, false)) {
|
||||
} else {
|
||||
fmt::print("Attempting to redefine type of symbol {} from {} to {}\n", name,
|
||||
skv->second.print(), type_spec.print());
|
||||
throw std::runtime_error("Type redefinition");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parse_type_defs(const std::vector<std::string>& file_path);
|
||||
|
||||
std::string dump_symbol_types();
|
||||
};
|
||||
|
||||
#endif // JAK_DECOMPILERTYPESYSTEM_H
|
||||
@@ -514,3 +514,14 @@ std::shared_ptr<Form> buildList(std::vector<std::shared_ptr<Form>>& forms) {
|
||||
}
|
||||
return buildList(forms.data(), forms.size());
|
||||
}
|
||||
|
||||
std::shared_ptr<Form> buildList(std::vector<std::string>& forms) {
|
||||
if (forms.empty()) {
|
||||
return gSymbolTable.getEmptyPair();
|
||||
}
|
||||
std::vector<std::shared_ptr<Form>> f;
|
||||
for (auto& x : forms) {
|
||||
f.push_back(toForm(x));
|
||||
}
|
||||
return buildList(f.data(), f.size());
|
||||
}
|
||||
@@ -122,6 +122,7 @@ std::shared_ptr<Form> buildList(const std::string& str);
|
||||
std::shared_ptr<Form> buildList(std::shared_ptr<Form> form);
|
||||
std::shared_ptr<Form> buildList(std::vector<std::shared_ptr<Form>>& forms);
|
||||
std::shared_ptr<Form> buildList(std::shared_ptr<Form>* forms, int count);
|
||||
std::shared_ptr<Form> buildList(std::vector<std::string>& forms);
|
||||
|
||||
template <typename... Args>
|
||||
std::shared_ptr<Form> buildList(const std::string& str, Args... rest) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Language Changes
|
||||
|
||||
## V0.1
|
||||
- The GOAL language version has been set to 0.1
|
||||
- Calling a function with unknown argument/return types is now an error instead of a warning
|
||||
- Getting a method of an object or type with `method` returns the correct type for methods using the `_type_` feature
|
||||
- The `object-new` macro will now type check arguments
|
||||
- The size argument to `(method object new)` is now an `int` instead of `int32`
|
||||
- Using `set!` incorrectly, like `(set! 1 2)` will now create an error instead of having no effect
|
||||
- GOOS now has a `fmt` form which wraps `libfmt` for doing string formatting.
|
||||
- GOOS now has an `error` form for throwing an error with a string to describe it
|
||||
- GOAL `if` now throws errors on extra arguments instead of silently ignoring them
|
||||
- The first 1 MB of GOAL memory now cannot be read/written/executed so dereferencing a GOAL null pointer will now segfault
|
||||
- The runtime now accepts command line boot arguments
|
||||
- The runtime now defaults to loading `KERNEL.CGO` and using its `kernel-dispatcher` function.
|
||||
- The runtime now accepts a `-nokernel` parameter for running without `KERNEL.CGO`.
|
||||
- The runtime will now refuse to load object files from another major GOAL version
|
||||
- Using `&+` and `&+!` now produces a pointer with the same type as the original.
|
||||
- There is a `&-` which returns a `uint` and works with basically any input types
|
||||
- The `&` operator works on fields and elements in arrays
|
||||
- The `&->` operator has been added
|
||||
@@ -0,0 +1,277 @@
|
||||
# GOAL Operations
|
||||
|
||||
## `div.s`
|
||||
Suspected source
|
||||
```
|
||||
(/ 1.0 x)
|
||||
```
|
||||
where `x` is in a GPR:
|
||||
```
|
||||
lwc1 f0, L345(fp) ;; first argument prepared first?
|
||||
mtc1 f1, a0 ;; second argument prepared second?
|
||||
div.s f0, f0, f1
|
||||
```
|
||||
Sequence
|
||||
- Compile first
|
||||
- First to FPR
|
||||
- Compile second
|
||||
- Second to FPR
|
||||
|
||||
## `daddu`
|
||||
Used for `int` and `uint` addition.
|
||||
|
||||
Two element form:
|
||||
```
|
||||
daddu v0, a0, a1
|
||||
```
|
||||
is `(+ a0 a1)` - the order in the opcode matches the order in the expression.
|
||||
|
||||
## `daddiu` to get a symbol
|
||||
```
|
||||
daddiu v0, s7, #t
|
||||
```
|
||||
Note for `#t`: `#t` is linked when the code literally has a `#t` in it. Other cases are currently unknown.
|
||||
|
||||
## `dsubu`
|
||||
Used for `int` and `uint` subtraction.
|
||||
|
||||
## `mult3` (EE `mult`)
|
||||
Used for `int` multiplication.
|
||||
|
||||
Like `daddu` for opcode ordering:
|
||||
```
|
||||
mult3 v0, a0, a1
|
||||
```
|
||||
is `(* a0 a1)`.
|
||||
|
||||
## `div`
|
||||
Used for `int` division.
|
||||
|
||||
```
|
||||
div a0, a1
|
||||
mflo v0
|
||||
```
|
||||
is `(/ a0 a1)`.
|
||||
|
||||
and also for `int` mod
|
||||
```
|
||||
div a0, a1
|
||||
mfhi v0
|
||||
```
|
||||
## `or` used to get the value of false
|
||||
```
|
||||
or v0, s7, r0
|
||||
```
|
||||
|
||||
## `or` used as a bitwise or
|
||||
```
|
||||
or v0, a0, a1
|
||||
```
|
||||
is `(logior a0 a1)`
|
||||
|
||||
## `and` used as a bitwise and
|
||||
```
|
||||
and v0, a0, a1
|
||||
```
|
||||
is `(logand a0 a1)`.
|
||||
|
||||
```
|
||||
(logand #xfffffff0 (+ (ash (-> thing field) 2) 43))
|
||||
```
|
||||
is
|
||||
```
|
||||
ld v1, L346(fp) ;; first arg to the and
|
||||
lhu a0, 14(a0) ;; second arg evaluation...
|
||||
dsll a0, a0, 2
|
||||
daddiu a0, a0, 43
|
||||
and v0, v1, a0 ;; and result, first, second
|
||||
```
|
||||
|
||||
## `nor` used as a bitwise nor
|
||||
```
|
||||
nor v0, a0, a1
|
||||
```
|
||||
is `(lognor a0 a1)`
|
||||
|
||||
## `xor` used as a bitwise xor
|
||||
```
|
||||
xor v0, a0, a1
|
||||
```
|
||||
is `(logxor a0 a1)`
|
||||
|
||||
## `nor` used as a logical not
|
||||
```
|
||||
nor v0, a0, r0
|
||||
```
|
||||
is `(lognot a0)`
|
||||
|
||||
|
||||
|
||||
# Common "Idioms"
|
||||
|
||||
## `ash`
|
||||
Variable shift (`ash`) is an inline function
|
||||
```
|
||||
or v1, a0, r0
|
||||
bgezl a1, L306
|
||||
dsllv v0, v1, a1
|
||||
|
||||
dsubu a0, r0, a1
|
||||
dsrav v0, v1, a0
|
||||
L306:
|
||||
```
|
||||
|
||||
## `abs` of integer
|
||||
```
|
||||
or v0, a0, r0
|
||||
bltzl v0, L302
|
||||
dsubu v0, r0, v0
|
||||
L302:
|
||||
```
|
||||
|
||||
## `min` of integers
|
||||
```
|
||||
or v0, a0, r0
|
||||
or v1, a1, r0
|
||||
slt a0, v0, v1
|
||||
movz v0, v1, a0
|
||||
```
|
||||
|
||||
## `max` of integers
|
||||
```
|
||||
or v0, a0, r0
|
||||
or v1, a1, r0
|
||||
slt a0, v0, v1
|
||||
movn v0, v1, a0
|
||||
```
|
||||
|
||||
# Others
|
||||
|
||||
## Integer constants that are large
|
||||
A constant of `0xfffffff0` is loaded with `ld`
|
||||
|
||||
## Access value of symbol
|
||||
Seems to always use `lw`?
|
||||
|
||||
# Control Flow Info
|
||||
|
||||
|
||||
## Begin-like forms flush everything always, immediately after compiling
|
||||
Example in `vector` with flushing:
|
||||
```
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; .function vector3s+!
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
L8:
|
||||
daddiu sp, sp, -16
|
||||
sd fp, 8(sp)
|
||||
or fp, t9, r0
|
||||
daddiu v1, fp, L109 ;; string: "Add 2 vectors3."
|
||||
lwc1 f0, 0(a1)
|
||||
lwc1 f1, 0(a2)
|
||||
add.s f0, f0, f1
|
||||
swc1 f0, 0(a0)
|
||||
lwc1 f0, 4(a1)
|
||||
lwc1 f1, 4(a2)
|
||||
add.s f0, f0, f1
|
||||
swc1 f0, 4(a0)
|
||||
lwc1 f0, 8(a1)
|
||||
lwc1 f1, 8(a2)
|
||||
add.s f0, f0, f1
|
||||
swc1 f0, 8(a0)
|
||||
or v0, a0, r0
|
||||
ld fp, 8(sp)
|
||||
jr ra
|
||||
daddiu sp, sp, 16
|
||||
```
|
||||
The `daddiu v1, fp, L109` loads a `string` into the `v1` register which is never used, immediately after the prologue. This will only happen if the value is flushed. This is very likely a documentation comment that accidentally got included as a string constant. It's unused, so there was likely no consumer of the string that did the `flush` - it was done by the top level evaluation.
|
||||
```
|
||||
(defun vector3s+! (stuff)
|
||||
"Add 2 vectors3." ;; oops, a string constant instead of a comment.
|
||||
... ; rest of the function
|
||||
)
|
||||
```
|
||||
## Return-From evaluates to 0 bug
|
||||
We would expect the value of `(return-from #f x)` to be nothing, as there's no possible way to use it. However, GOAL seems to have a small bug where `(return-from #f x)` always attempts to evaluate to 0. This would be like implementing it as:
|
||||
```lisp
|
||||
(set! retrun-reg return-value)
|
||||
(goto end-of-function)
|
||||
0 ;; oops
|
||||
```
|
||||
by accident.
|
||||
|
||||
Example in GOAL:
|
||||
|
||||
```
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; .function basic-type? (in gcommon)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
L285:
|
||||
lwu v1, -4(a0)
|
||||
lw a0, object(s7)
|
||||
L286:
|
||||
bne v1, a1, L287
|
||||
or a2, s7, r0
|
||||
; (return-from #f #t) starts here
|
||||
daddiu v1, s7, #t ; compile/flush the #t
|
||||
or v0, v1, r0 ; move to return register
|
||||
beq r0, r0, L288 ; branch to end
|
||||
sll r0, r0, 0 ; branch delay slot (usual filler)
|
||||
|
||||
or v1, r0, r0 ; unreachable loading of 0 into a register.
|
||||
L287:
|
||||
lwu v1, 4(v1)
|
||||
bne v1, a0, L286
|
||||
sll r0, r0, 0
|
||||
|
||||
or v0, s7, r0
|
||||
L288:
|
||||
jr ra
|
||||
daddu sp, sp, r0
|
||||
|
||||
sll r0, r0, 0
|
||||
sll r0, r0, 0
|
||||
```
|
||||
|
||||
## Unused else case returning false in cond
|
||||
From `delete!` in gcommon.
|
||||
```
|
||||
beq a2, a0, L222 ; (if (= a2 a0) only-one-case)
|
||||
or a0, s7, r0 ; a0 is unused return value of if
|
||||
|
||||
lw a0, 2(a2) ; (set! (cdr v1) (cdr a2)), will evaluate to (cdr a2) which is stored in a0
|
||||
sw a0, 2(v1)
|
||||
L222: ; a0 = #f or a0 = (cdr a2) depending on branch taken, but it's totally unused!
|
||||
or v0, a1, r0 ; return a1
|
||||
jr ra
|
||||
daddu sp, sp, r0
|
||||
```
|
||||
Also note that all cases stored their result in `a0`, even though nothing uses the result.
|
||||
|
||||
## Function Calls evaluate arguments in order:
|
||||
```
|
||||
lw t9, format(s7) ;; head of function
|
||||
daddiu a0, s7, #t ;; first arg
|
||||
daddiu a1, fp, L344 ;; second arg
|
||||
sllv a2, gp, r0 ;; third arg
|
||||
dsra32 a3, gp, 0 ;; fourth arg
|
||||
pcpyud v1, gp, r0
|
||||
sllv t0, v1, r0 ;; fifth arg
|
||||
pcpyud v1, gp, r0
|
||||
dsra32 t1, v1, 0 ;; sixth arg
|
||||
por t2, gp, r0 ;; seventh arg
|
||||
jalr ra, t9
|
||||
sll v0, ra, 0
|
||||
```
|
||||
also an example of lack of common subexpression elimination on the `pcpyud v1, gp, r0`s.
|
||||
|
||||
### A second example with register type conversions:
|
||||
```
|
||||
lw t9, format(s7) ;; function
|
||||
daddiu a0, s7, #t ;; first arg
|
||||
daddiu a1, fp, L343 ;; second arg
|
||||
lwc1 f0, 0(gp) ;; compile and flush third arg
|
||||
mfc1 a2, f0 ;; move to correct reg type
|
||||
jalr ra, t9
|
||||
sll v0, ra, 0
|
||||
```
|
||||
+1001
File diff suppressed because it is too large
Load Diff
+11
-3
@@ -70,16 +70,24 @@ set(RUNTIME_SOURCE
|
||||
overlord/stream.cpp)
|
||||
|
||||
# the runtime should be built without any static/dynamic libraries.
|
||||
add_executable(gk ${RUNTIME_SOURCE} main.cpp)
|
||||
#add_executable(gk ${RUNTIME_SOURCE} main.cpp)
|
||||
|
||||
|
||||
# we also build a runtime library for testing. This version is likely unable to call GOAL code correctly, but
|
||||
# can be used to test other things.
|
||||
add_library(runtime ${RUNTIME_SOURCE})
|
||||
|
||||
add_executable(gk main.cpp)
|
||||
|
||||
IF (WIN32)
|
||||
# set stuff for windows
|
||||
target_link_libraries(gk cross_sockets mman common_util)
|
||||
target_link_libraries(runtime mman cross_sockets common_util)
|
||||
target_link_libraries(gk cross_sockets mman common_util runtime)
|
||||
ELSE()
|
||||
# set stuff for other systems
|
||||
target_link_libraries(gk cross_sockets pthread common_util)
|
||||
target_link_libraries(runtime pthread cross_sockets common_util)
|
||||
target_link_libraries(gk cross_sockets pthread common_util runtime)
|
||||
|
||||
ENDIF()
|
||||
|
||||
|
||||
|
||||
+13
-7
@@ -47,12 +47,15 @@ u32 DebugSegment;
|
||||
// Set to 1 to load game engine after boot automatically
|
||||
u32 DiskBoot;
|
||||
|
||||
u32 MasterUseKernel;
|
||||
|
||||
void kboot_init_globals() {
|
||||
strcpy(DebugBootLevel, "#f"); // no specified level
|
||||
strcpy(DebugBootMessage, "play"); // play mode, the default retail mode
|
||||
|
||||
MasterExit = 0;
|
||||
MasterDebug = 1;
|
||||
MasterUseKernel = 1;
|
||||
DebugSegment = 1;
|
||||
DiskBoot = 0;
|
||||
memset(&masterConfig, 0, sizeof(MasterConfig));
|
||||
@@ -117,6 +120,9 @@ s32 goal_main(int argc, const char* const* argv) {
|
||||
if (InitMachine() >= 0) { // init kernel
|
||||
KernelCheckAndDispatch(); // run kernel
|
||||
ShutdownMachine(); // kernel died, we should too.
|
||||
} else {
|
||||
fprintf(stderr, "InitMachine failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -143,15 +149,15 @@ void KernelCheckAndDispatch() {
|
||||
call_goal(Ptr<Function>(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem);
|
||||
} else {
|
||||
if (ListenerFunction->value != s7.offset) {
|
||||
fprintf(stderr, "Running Listener Function:\n");
|
||||
auto cptr = Ptr<u8>(ListenerFunction->value).c();
|
||||
for (int i = 0; i < 40; i++) {
|
||||
fprintf(stderr, "%x ", cptr[i]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
// fprintf(stderr, "Running Listener Function:\n");
|
||||
// auto cptr = Ptr<u8>(ListenerFunction->value).c();
|
||||
// for (int i = 0; i < 40; i++) {
|
||||
// fprintf(stderr, "%x ", cptr[i]);
|
||||
// }
|
||||
// fprintf(stderr, "\n");
|
||||
auto result =
|
||||
call_goal(Ptr<Function>(ListenerFunction->value), 0, 0, 0, s7.offset, g_ee_main_mem);
|
||||
fprintf(stderr, "result of listener function: %lld\n", result);
|
||||
// fprintf(stderr, "result of listener function: %lld\n", result);
|
||||
#ifdef __linux__
|
||||
cprintf("%ld\n", result);
|
||||
#else
|
||||
|
||||
+1
-1
@@ -75,6 +75,6 @@ void KernelCheckAndDispatch();
|
||||
*/
|
||||
void KernelShutdown();
|
||||
|
||||
constexpr bool MasterUseKernel = false;
|
||||
extern u32 MasterUseKernel;
|
||||
|
||||
#endif // RUNTIME_KBOOT_H
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <common/versions.h>
|
||||
#include "klink.h"
|
||||
#include "fileio.h"
|
||||
#include "kscheme.h"
|
||||
@@ -67,6 +68,14 @@ void link_control::begin(Ptr<uint8_t> object_file,
|
||||
m_segment_process = 0;
|
||||
|
||||
ObjectFileHeader* ofh = m_link_block_ptr.cast<ObjectFileHeader>().c();
|
||||
if (ofh->goal_version_major != versions::GOAL_VERSION_MAJOR) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"VERSION ERROR: C Kernel built from GOAL %d.%d, but object file %s is from GOAL %d.%d\n",
|
||||
versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR, name, ofh->goal_version_major,
|
||||
ofh->goal_version_minor);
|
||||
exit(0);
|
||||
}
|
||||
if (link_debug_printfs) {
|
||||
printf("Object file header:\n");
|
||||
printf(" GOAL ver %d.%d obj %d len %d\n", ofh->goal_version_major, ofh->goal_version_minor,
|
||||
|
||||
@@ -149,7 +149,6 @@ void ProcessListenerMessage(Ptr<char> msg) {
|
||||
// this setup allows listener function execution to clean up after itself.
|
||||
ListenerFunction->value =
|
||||
link_and_exec(buffer, "*listener*", 0, kdebugheap, LINK_FLAG_FORCE_DEBUG).offset;
|
||||
fprintf(stderr, "ListenerFunction is now 0x%x\n", ListenerFunction->value);
|
||||
return; // don't ack yet, this will happen after the function runs.
|
||||
} break;
|
||||
default:
|
||||
|
||||
@@ -94,6 +94,12 @@ void InitParms(int argc, const char* const* argv) {
|
||||
reboot = 0;
|
||||
}
|
||||
|
||||
// an added mode to allow booting without a KERNEL.CGO for testing
|
||||
if (arg == "-nokernel") {
|
||||
Msg(6, "dkernel: no kernel mode\n");
|
||||
MasterUseKernel = false;
|
||||
}
|
||||
|
||||
// GOAL Settings
|
||||
// ----------------------------
|
||||
|
||||
|
||||
+20
-18
@@ -361,22 +361,26 @@ Ptr<Function> make_function_from_c_win32(void* func) {
|
||||
}
|
||||
|
||||
/*
|
||||
* push rdi
|
||||
* push rsi
|
||||
* push rdx
|
||||
* push rcx
|
||||
* pop r9
|
||||
* pop r8
|
||||
* pop rdx
|
||||
* pop rcx
|
||||
*
|
||||
* sub rsp, 40
|
||||
* call rax
|
||||
* add rsp, 40
|
||||
* ret
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdx
|
||||
pop rcx
|
||||
push r10
|
||||
push r11
|
||||
sub rsp, 40
|
||||
call rax
|
||||
add rsp, 40
|
||||
pop r11
|
||||
pop r10
|
||||
ret
|
||||
*/
|
||||
for (auto x : {0x57, 0x56, 0x52, 0x51, 0x41, 0x59, 0x41, 0x58, 0x5A, 0x59, 0x48,
|
||||
0x83, 0xEC, 0x28, 0xFF, 0xD0, 0x48, 0x83, 0xC4, 0x28, 0xC3}) {
|
||||
for (auto x :
|
||||
{0x57, 0x56, 0x52, 0x51, 0x41, 0x59, 0x41, 0x58, 0x5A, 0x59, 0x41, 0x52, 0x41, 0x53, 0x48,
|
||||
0x83, 0xEC, 0x28, 0xFF, 0xD0, 0x48, 0x83, 0xC4, 0x28, 0x41, 0x5B, 0x41, 0x5A, 0xC3}) {
|
||||
mem.c()[i++] = x;
|
||||
}
|
||||
|
||||
@@ -883,8 +887,6 @@ u64 method_set(u32 type_, u32 method_id, u32 method) {
|
||||
if (method_id > 127)
|
||||
printf("[METHOD SET ERROR] tried to set method %d\n", method_id);
|
||||
|
||||
// printf("METHOD SET id %d to 0x%x type 0x%x!\n", method_id, method, type_);
|
||||
|
||||
auto existing_method = type->get_method(method_id).offset;
|
||||
|
||||
if (method == 1) {
|
||||
@@ -1089,7 +1091,7 @@ u64 sprint(u32 obj) {
|
||||
*/
|
||||
u64 print_object(u32 obj) {
|
||||
if ((obj & OFFSET_MASK) == BINTEGER_OFFSET) {
|
||||
return print_binteger(obj);
|
||||
return print_binteger(s64(s32(obj)));
|
||||
} else {
|
||||
if ((obj < SymbolTable2.offset || 0x7ffffff < obj) && // not in normal memory
|
||||
(obj < 0x84000 || 0x100000 <= obj)) { // not in kernel memory
|
||||
|
||||
+11
-7
@@ -49,6 +49,9 @@ u8* g_ee_main_mem = nullptr;
|
||||
|
||||
namespace {
|
||||
|
||||
int g_argc = 0;
|
||||
char** g_argv = nullptr;
|
||||
|
||||
/*!
|
||||
* SystemThread function for running the DECI2 communication with the GOAL compiler.
|
||||
*/
|
||||
@@ -98,10 +101,6 @@ constexpr u64 EE_MAIN_MEM_MAP = 0x2000000000; // intentionally > 32-bit to
|
||||
// so this should be used only for debugging.
|
||||
constexpr bool EE_MEM_LOW_MAP = false;
|
||||
|
||||
// GOAL Boot arguments
|
||||
constexpr const char* GOAL_ARGV[] = {"", "-fakeiso", "-boot", "-debug"};
|
||||
constexpr int GOAL_ARGC = 4;
|
||||
|
||||
/*!
|
||||
* SystemThread Function for the EE (PS2 Main CPU)
|
||||
*/
|
||||
@@ -132,6 +131,11 @@ void ee_runner(SystemThreadInterface& iface) {
|
||||
|
||||
printf("[EE] Run!\n");
|
||||
memset((void*)g_ee_main_mem, 0, EE_MAIN_MEM_SIZE);
|
||||
|
||||
// prevent access to the first 1 MB of memory.
|
||||
// On the PS2 this is the kernel and can't be accessed either.
|
||||
// this may not work well on systems with a page size > 1 MB.
|
||||
mprotect((void*)g_ee_main_mem, 1024 * 1024, PROT_NONE);
|
||||
fileio_init_globals();
|
||||
kboot_init_globals();
|
||||
kdgo_init_globals();
|
||||
@@ -146,7 +150,7 @@ void ee_runner(SystemThreadInterface& iface) {
|
||||
kmemcard_init_globals();
|
||||
kprint_init_globals();
|
||||
|
||||
goal_main(GOAL_ARGC, GOAL_ARGV);
|
||||
goal_main(g_argc, g_argv);
|
||||
printf("[EE] Done!\n");
|
||||
|
||||
// // kill the IOP todo
|
||||
@@ -224,8 +228,8 @@ void iop_runner(SystemThreadInterface& iface) {
|
||||
* Arguments are currently ignored.
|
||||
*/
|
||||
u32 exec_runtime(int argc, char** argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
g_argc = argc;
|
||||
g_argv = argv;
|
||||
|
||||
// step 1: sce library prep
|
||||
iop::LIBRARY_INIT();
|
||||
|
||||
@@ -194,8 +194,8 @@ void Deci2Server::run() {
|
||||
}
|
||||
|
||||
auto* hdr = (Deci2Header*)(buffer);
|
||||
fprintf(stderr, "[DECI2] Got message:\n");
|
||||
fprintf(stderr, " %d %d 0x%x %c -> %c\n", hdr->len, hdr->rsvd, hdr->proto, hdr->src, hdr->dst);
|
||||
fprintf(stderr, "[DECI2] Got message: %d %d 0x%x %c -> %c\n", hdr->len, hdr->rsvd, hdr->proto,
|
||||
hdr->src, hdr->dst);
|
||||
|
||||
hdr->rsvd = got;
|
||||
|
||||
|
||||
@@ -91,6 +91,9 @@ void IOP::kill_from_ee() {
|
||||
void IOP::signal_run_iop() {
|
||||
std::unique_lock<std::mutex> lk(iters_mutex);
|
||||
iop_iters_des += 100; // todo, tune this
|
||||
if (iop_iters_des - iop_iters_act > 500) {
|
||||
iop_iters_des = iop_iters_act + 500;
|
||||
}
|
||||
iop_run_cv.notify_all();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# Directory of this script
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
$DIR/build/game/gk "$@"
|
||||
$DIR/build/game/gk -fakeiso -debug -nokernel
|
||||
|
||||
+202
-1
@@ -92,6 +92,12 @@
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro shutdown-target ()
|
||||
`(begin
|
||||
(reset-target :shutdown)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
;; GOAL Syntax
|
||||
@@ -138,6 +144,26 @@
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro until (test &rest body)
|
||||
(with-gensyms (reloop)
|
||||
`(begin
|
||||
(label ,reloop)
|
||||
,@body
|
||||
(when-goto (not ,test) ,reloop)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro dotimes (var &rest body)
|
||||
`(let (( ,(first var) 0))
|
||||
(while (< ,(first var) ,(second var))
|
||||
,@body
|
||||
(+1! ,(first var))
|
||||
)
|
||||
,@(cddr var)
|
||||
)
|
||||
)
|
||||
|
||||
;; Backup some values, and restore after executing body.
|
||||
;; Non-dynamic (nonlocal jumps out of body will skip restore)
|
||||
(defmacro protect (defs &rest body)
|
||||
@@ -165,8 +191,11 @@
|
||||
`(set! ,place (+ ,place ,amount))
|
||||
)
|
||||
|
||||
;; todo, handle too many arguments correct
|
||||
(defmacro if (condition true-case &rest others)
|
||||
(if (> (length others) 1)
|
||||
(error "got too many arguments to if")
|
||||
#f
|
||||
)
|
||||
(if (null? others)
|
||||
`(cond (,condition ,true-case))
|
||||
`(cond (,condition ,true-case)
|
||||
@@ -175,6 +204,62 @@
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro when (condition &rest body)
|
||||
`(if ,condition
|
||||
(begin ,@body)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro unless (condition &rest body)
|
||||
`(if (not ,condition)
|
||||
(begin ,@body)
|
||||
)
|
||||
)
|
||||
|
||||
;; TODO - these work but aren't very efficient.
|
||||
|
||||
(defmacro and (&rest args)
|
||||
(with-gensyms (result end)
|
||||
`(begin
|
||||
(let ((,result (the object #f)))
|
||||
,@(apply (lambda (x)
|
||||
`(begin
|
||||
(set! ,result ,x)
|
||||
(if (eq? ,result #f)
|
||||
(goto ,end)
|
||||
)
|
||||
)
|
||||
)
|
||||
args
|
||||
)
|
||||
(label ,end)
|
||||
,result
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro or (&rest args)
|
||||
(with-gensyms (result end)
|
||||
`(begin
|
||||
(let ((,result (the object #f)))
|
||||
,@(apply (lambda (x)
|
||||
`(begin
|
||||
(set! ,result ,x)
|
||||
(if (not (eq? ,result #f))
|
||||
(goto ,end)
|
||||
)
|
||||
)
|
||||
)
|
||||
args
|
||||
)
|
||||
(label ,end)
|
||||
,result
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
;; Math Macros
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
@@ -201,4 +286,120 @@
|
||||
|
||||
(defmacro 1- (var)
|
||||
`(- ,var 1)
|
||||
)
|
||||
|
||||
(defmacro zero? (thing)
|
||||
`(eq? ,thing 0)
|
||||
)
|
||||
|
||||
(defmacro &+! (val amount)
|
||||
`(set! ,val (&+ ,val ,amount))
|
||||
)
|
||||
|
||||
(defmacro &- (a b)
|
||||
`(- (the-as uint ,a) (the-as uint ,b))
|
||||
)
|
||||
|
||||
(defmacro &-> (&rest args)
|
||||
`(& (-> ,@args))
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Bit Macros
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(defmacro align16 (value)
|
||||
`(logand #xfffffff0 (+ (the-as integer ,value) 15))
|
||||
)
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TYPE STUFF
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmacro basic? (obj)
|
||||
;; todo, make this more efficient
|
||||
`(= 4 (logand (the integer ,obj) #b111))
|
||||
)
|
||||
|
||||
(defmacro pair? (obj)
|
||||
;; todo, make this more efficient
|
||||
`(= 2 (logand (the integer ,obj) #b111))
|
||||
)
|
||||
|
||||
(defmacro binteger? (obj)
|
||||
`(zero? (logand (the integer ,obj) #b111))
|
||||
)
|
||||
|
||||
(defmacro rtype-of (obj)
|
||||
`(cond ((binteger? ,obj) binteger)
|
||||
((pair? ,obj) pair)
|
||||
(else (-> (the basic ,obj) type))
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; PAIR STUFF
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(defmacro cons (a b)
|
||||
`(new 'global 'pair ,a ,b)
|
||||
)
|
||||
|
||||
(defmacro list (&rest args)
|
||||
(if (null? args)
|
||||
(quote '())
|
||||
`(cons ,(car args) (list ,@(cdr args)))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro null? (arg)
|
||||
;; todo, make this better
|
||||
`(eq? ,arg '())
|
||||
)
|
||||
(defmacro caar (arg)
|
||||
`(car (car ,arg))
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; METHOD STUFF
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(defmacro object-new (&rest sz)
|
||||
(if (null? sz)
|
||||
`(the ,(current-method-type) ((method object new) allocation type-to-make (the int (-> type-to-make size))))
|
||||
`(the ,(current-method-type)((method object new) allocation type-to-make ,@sz))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TEST STUFF
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmacro expect-eq (a b &key (name "unknown"))
|
||||
`(if (!= ,a ,b)
|
||||
(format #t "Test Failed On Test ~D: ~A~%" *test-count* ,name)
|
||||
(+! *test-count* 1)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro expect-true (a)
|
||||
`(expect-eq ,a #t)
|
||||
)
|
||||
|
||||
(defmacro expect-false (a)
|
||||
`(expect-eq ,a #f)
|
||||
)
|
||||
|
||||
(defmacro start-test (test-name)
|
||||
`(begin
|
||||
(define *test-name* ,test-name)
|
||||
(define *test-count* 0)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro finish-test ()
|
||||
`(format #t "Test ~A: ~D Passes~%" *test-name* *test-count*)
|
||||
)
|
||||
@@ -71,7 +71,7 @@
|
||||
(define-extern load (function string kheap object))
|
||||
(define-extern loado (function string kheap object))
|
||||
(define-extern unload (function string none))
|
||||
(define-extern _format function)
|
||||
(define-extern _format (function _varargs_ object))
|
||||
(define-extern malloc (function kheap int pointer))
|
||||
(define-extern kmalloc (function kheap int int string))
|
||||
(define-extern new-dynamic-structure (function kheap type int structure))
|
||||
@@ -101,7 +101,7 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; *listener-link-block*
|
||||
;; *listener-function*
|
||||
(define-extern *listener-function* (function object))
|
||||
;; kernel-dispatcher
|
||||
;; kernel-packages
|
||||
;; *print-column*
|
||||
|
||||
@@ -259,8 +259,386 @@
|
||||
)
|
||||
|
||||
|
||||
;; The "print" method of a type should print out a single line representation of the object.
|
||||
;; The default print method for a basic will be something like #<my-type @ #xbeef>
|
||||
;; This is used when printing an object with format, using the "~A" format specification.
|
||||
;; And of course in functions like print, printl.
|
||||
(defmethod print bfloat ((obj bfloat))
|
||||
"Override the default print method to print a bfloat like a normal float"
|
||||
(format #t "~f" (-> obj data))
|
||||
obj
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Type System
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; The asize-of method should return the total size in memory used by an object.
|
||||
; It's used for traversing heaps of basics and copying basics.
|
||||
; Most basic/structure types are "fixed size", and their default asize-of method will simply
|
||||
; return the "size" field of their type, so you don't have to worry about it.
|
||||
; However, some types are dynamic (like a string) and require that you provide your own method.
|
||||
; A common approach is to have an "allocated-length" field, then have the asize-of method return
|
||||
; (+ (-> obj type size) (* elem-size (-> obj allocated-length)))
|
||||
; asize-of returns the actual size, including the type field, and can have any alignment.
|
||||
|
||||
;; A "type" object contains some basic information about a type as well as the list of methods.
|
||||
;; Some types have more methods than others, so the method table makes "type" a dynamic type.
|
||||
;; As a result, we should define an "asize-of" method for type. It's possibly unused because it's wrong.
|
||||
|
||||
(defmethod asize-of type ((obj type))
|
||||
"Get the size in memory of a type"
|
||||
;; The 28 is 8 bytes too large. It's also strange that types have a 16-byte aligned size always,
|
||||
;; but this matches what the runtime does as well. There's no reason that I can see for this,
|
||||
;; as other basics don't require 16-byte aligned sizes.
|
||||
(align16 (+ 28 (* 4 (-> type allocated-length))))
|
||||
)
|
||||
|
||||
(defun basic-type? ((obj basic) (input-type type))
|
||||
"Is obj an object of type input-type, or of child type of input-type?
|
||||
Note: checking if a basic is of type object will return #f."
|
||||
(let ((basics-type (-> obj type))
|
||||
(object-type object))
|
||||
(until (eq? (set! basics-type (-> basics-type parent)) object-type)
|
||||
(if (eq? basics-type input-type)
|
||||
;; return-from #f will return from the function with the value of #t
|
||||
(return-from #f #t)
|
||||
)
|
||||
)
|
||||
)
|
||||
#f ;; didn't find it, return false
|
||||
)
|
||||
|
||||
(defun type-type? ((a type) (b type))
|
||||
"is a a type (or child type) of type b?"
|
||||
(until (eq? a object)
|
||||
;; it's not clear why a might be zero?
|
||||
;; perhaps if the type system is not yet initialized fully for the type?
|
||||
(if (or (eq? a b) (zero? a))
|
||||
(return-from #f #t)
|
||||
)
|
||||
(set! a (-> a parent))
|
||||
)
|
||||
#f
|
||||
)
|
||||
|
||||
(defun find-parent-method ((the-type type) (method-id int))
|
||||
"Find the nearest parent which has a different method, and get that method.
|
||||
Use with extreme caution - if a checked parent has fewer methods than the child, it will
|
||||
access out-of-bounds memory. Returns the nothing function if it gets to the top and
|
||||
the parent has the same type, or if any parent has 0 as a method."
|
||||
(let* ((child-method (-> the-type method-table method-id))
|
||||
(parent-method child-method)
|
||||
)
|
||||
|
||||
;; keep looking until we find a different parent method
|
||||
(until (not (eq? parent-method child-method))
|
||||
;; at the top of the type tree.
|
||||
(if (eq? the-type object)
|
||||
(return-from #f nothing)
|
||||
)
|
||||
|
||||
(set! the-type (-> the-type parent))
|
||||
(set! parent-method (-> the-type method-table method-id))
|
||||
(if (eq? 0 (the int parent-method))
|
||||
(return-from #f nothing)
|
||||
)
|
||||
)
|
||||
parent-method
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; pair and list
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun ref ((obj object) (idx int))
|
||||
"Get the nth item from a list. No type checking or range checking is done, so be careful!"
|
||||
(dotimes (i idx (car obj))
|
||||
(set! obj (cdr obj))
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod length pair ((obj pair))
|
||||
"Get the number of elements in a proper list"
|
||||
(if (eq? obj '())
|
||||
(return-from #f 0)
|
||||
)
|
||||
|
||||
(let ((lst (cdr obj))
|
||||
(len 1))
|
||||
(while (and (not (eq? lst '()))
|
||||
(pair? lst)
|
||||
)
|
||||
(+1! len)
|
||||
(set! lst (cdr lst))
|
||||
)
|
||||
len)
|
||||
)
|
||||
|
||||
|
||||
|
||||
(defmethod asize-of pair ((obj pair))
|
||||
"Get the asize of a pair"
|
||||
(the-as int (-> pair size))
|
||||
)
|
||||
|
||||
(defun last ((obj object))
|
||||
"Get the last pair in a list."
|
||||
(while (not (eq? (cdr obj) '()))
|
||||
(set! obj (cdr obj))
|
||||
)
|
||||
obj
|
||||
)
|
||||
|
||||
(defun member ((obj object) (lst object))
|
||||
"if obj is a member of the list, return the pair containing obj as its car.
|
||||
if not, return #f."
|
||||
(while (and (not (eq? lst '()))
|
||||
(not (eq? (car lst) obj)))
|
||||
(set! lst (cdr lst))
|
||||
)
|
||||
|
||||
(if (eq? lst '())
|
||||
#f
|
||||
lst
|
||||
)
|
||||
)
|
||||
|
||||
(define-extern name= (function basic basic symbol))
|
||||
|
||||
(defun nmember ((obj basic) (lst object))
|
||||
"If obj is a member of the list, return the pair containing obj as its car.
|
||||
If not, return #f. Use name= (see gstring.gc) to check equality."
|
||||
(while (and (not (eq? lst '()))
|
||||
(not (name= (the basic (car lst)) obj))
|
||||
)
|
||||
(set! lst (cdr lst))
|
||||
)
|
||||
|
||||
(if (eq? lst '())
|
||||
#f
|
||||
lst
|
||||
)
|
||||
)
|
||||
|
||||
(defun assoc ((item object) (alst object))
|
||||
"Get a pair with car of item from the association list (list of pairs) alst."
|
||||
(while (and (not (null? alst))
|
||||
(not (eq? (caar alst) item)))
|
||||
(set! alst (cdr alst))
|
||||
)
|
||||
(if (not (null? alst))
|
||||
(car alst)
|
||||
#f
|
||||
)
|
||||
)
|
||||
|
||||
(defun assoce ((item object) (alst object))
|
||||
"Like assoc, but a pair with car of 'else will match anything"
|
||||
(while (and (not (null? alst))
|
||||
(not (eq? (caar alst) item))
|
||||
(not (eq? (caar alst) 'else))
|
||||
)
|
||||
(set! alst (cdr alst))
|
||||
)
|
||||
(if (not (null? alst))
|
||||
(car alst)
|
||||
#f
|
||||
)
|
||||
)
|
||||
|
||||
;; todo
|
||||
;; nassoc
|
||||
;; nassce
|
||||
|
||||
(defun append! ((front object) (back object))
|
||||
"Append back to front."
|
||||
(if (null? front)
|
||||
(return-from #f back)
|
||||
)
|
||||
|
||||
(let ((lst front))
|
||||
;; seek to the end of front
|
||||
(while (not (null? (cdr lst)))
|
||||
(set! lst (cdr lst))
|
||||
)
|
||||
|
||||
;; this check seems not needed
|
||||
(if (not (null? lst))
|
||||
(set! (cdr lst) back)
|
||||
)
|
||||
|
||||
front
|
||||
)
|
||||
)
|
||||
|
||||
(defun delete! ((item object) (lst object))
|
||||
"Delete the first occurance of item from a list and return the list.
|
||||
Does nothing if the item isn't in the list."
|
||||
(if (eq? (car lst) item)
|
||||
(return-from #f (cdr lst))
|
||||
)
|
||||
|
||||
(let ((iter (cdr lst))
|
||||
(rep lst))
|
||||
|
||||
(while (and (not (null? iter))
|
||||
(not (eq? (car iter) item)))
|
||||
(set! rep iter)
|
||||
(set! iter (cdr iter))
|
||||
)
|
||||
|
||||
(if (not (null? iter))
|
||||
(set! (cdr rep) (cdr iter))
|
||||
)
|
||||
)
|
||||
(the pair lst)
|
||||
)
|
||||
|
||||
(defun delete-car! ((item object) (lst object))
|
||||
"Like delete, but will delete if (car item-from-list) is equal to item. Useful for deleting from association list by key."
|
||||
;(format #t "call to delete car: ~A ~A~%" item lst)
|
||||
(if (eq? (caar lst) item)
|
||||
(return-from #f (cdr lst))
|
||||
)
|
||||
|
||||
(let ((rep lst)
|
||||
(iter (cdr lst)))
|
||||
(while (and (not (null? iter))
|
||||
(not (eq? (caar iter) item)))
|
||||
(set! rep iter)
|
||||
(set! iter (cdr iter))
|
||||
)
|
||||
|
||||
(if (not (null? iter))
|
||||
(set! (cdr rep) (cdr iter))
|
||||
)
|
||||
)
|
||||
lst
|
||||
)
|
||||
|
||||
(defun insert-cons! ((kv object) (alst object))
|
||||
"Insert key-value pair into an association list. Also removes the old one if it was there."
|
||||
(cons kv (delete-car! (car kv) alst))
|
||||
)
|
||||
|
||||
(defun sort ((lst object) (compare (function object object object)))
|
||||
"Sort the given list in place. Uses the given comparison function. The comparison function can
|
||||
either return #t/#f or an integer, in which case the sign of the integer determines lt/gt."
|
||||
;; in each iteration, we count how many changes we make. Once we make no changes, the list is sorted.
|
||||
(let ((changes -1))
|
||||
|
||||
(while (not (zero? changes)) ;; outer loop
|
||||
(set! changes 0) ;; reset changes for this iteration
|
||||
(let ((iter lst)) ;; iterate through list
|
||||
(while (and (not (null? (cdr iter)))
|
||||
(pair? (cdr iter)))
|
||||
;; L221
|
||||
(let* ((val1 (car iter)) ;; value at iterator
|
||||
(val2 (car (cdr iter))) ;; value after iterator
|
||||
(c-result (compare val1 val2))) ;; run comparison function
|
||||
;; check if val1 and val2 are in order. The compare function may either return #t
|
||||
;; or it may return val1 - val2. There is an issue if val1 - val2 happens to equal #t or #f.
|
||||
(unless (or
|
||||
(and c-result (<= (the integer c-result) 0)) ;; not #f, and negative, we're sorted!
|
||||
(eq? c-result #t) ;; explictly return #t, we're sorted!
|
||||
)
|
||||
;; these two aren't sorted! so we swap them and increment changes.
|
||||
(+1! changes)
|
||||
(set! (car iter) val2)
|
||||
(set! (car (cdr iter)) val1)
|
||||
)
|
||||
;; move on to the next thing in the list.
|
||||
(set! iter (cdr iter))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
lst
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; inline array
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; a parent class for boxed "inline arrays" classes,
|
||||
;; An inline-array is an array with a bunch of objects back-to-back, as opposed to a bunch of
|
||||
;; references back to back.
|
||||
;; Most inline-arrays are unboxed and are just data - this is a somewhat rarely used container parent
|
||||
;; class for a class that wraps an unboxed inline-array.
|
||||
;; the "heap-base" field of the type is used to store the indexing scale.
|
||||
|
||||
(deftype inline-array-class (basic)
|
||||
((length int32 :offset-assert 4)
|
||||
(allocated-length int32 :offset-assert 8)
|
||||
(data uint8 :dynamic)
|
||||
)
|
||||
(:methods (new (symbol type int) _type_ 0) ;; we will override print later on. This is optional to include
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod new inline-array-class ((allocation symbol) (type-to-make type) (cnt int))
|
||||
"Create a new inline-array. Sets the length, allocated-length to cnt. Uses the mysterious heap-base field
|
||||
of the type-to-make to determine the element size"
|
||||
(let* ((sz (+ (-> type-to-make size) (* (-> type-to-make heap-base) cnt)))
|
||||
(new-object (object-new (the int sz))))
|
||||
;;(format 0 "create sz ~d at #x~X~%" sz new-object)
|
||||
(unless (zero? new-object)
|
||||
(set! (-> new-object length) cnt)
|
||||
(set! (-> new-object allocated-length) cnt)
|
||||
)
|
||||
new-object
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod length inline-array-class ((obj inline-array-class))
|
||||
;"Get the length of an inline-array"
|
||||
(-> obj length)
|
||||
)
|
||||
|
||||
(defmethod asize-of inline-array-class ((obj inline-array-class))
|
||||
;"Get the size in memory of an inline-array-class"
|
||||
(+ (the-as int (-> obj type size))
|
||||
(* (-> obj allocated-length) (-> obj type heap-base))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; array (todo)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; memcpy and similar
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(defun mem-copy! ((dst pointer) (src pointer) (size integer))
|
||||
"Copy memory from src to dst. Size is in bytes. This is not an efficient implementation,
|
||||
however, there are _no restrictions_ on size, alignment etc. Increasing address copy."
|
||||
(let ((i 0)
|
||||
(d (the pointer dst))
|
||||
(s (the pointer src))
|
||||
)
|
||||
|
||||
(while (< i size)
|
||||
(set! (-> (the (pointer uint8) d) 0) (-> (the (pointer uint8) s) 0))
|
||||
(&+! d 1)
|
||||
(&+! s 1)
|
||||
(+1! i)
|
||||
)
|
||||
)
|
||||
dst
|
||||
)
|
||||
|
||||
(defun mem-set32! ((dst pointer) (value int) (n int))
|
||||
"Memset a 32-bit value n times. Total memory filled is 4 * n bytes."
|
||||
(let ((p (the pointer dst))
|
||||
(i 0))
|
||||
(while (< i n)
|
||||
(set! (-> (the (pointer int32) p) 0) value)
|
||||
(&+! p 4)
|
||||
(+1! i)
|
||||
)
|
||||
)
|
||||
dst
|
||||
)
|
||||
@@ -5,3 +5,5 @@
|
||||
;; name in dgo: gkernel-h
|
||||
;; dgos: KERNEL
|
||||
|
||||
(defglobalconstant *kernel-major-version* 2)
|
||||
(defglobalconstant *kernel-minor-version* 0)
|
||||
@@ -5,3 +5,23 @@
|
||||
;; name in dgo: gkernel
|
||||
;; dgos: KERNEL
|
||||
|
||||
(define *kernel-version* (the binteger (logior (ash *kernel-major-version* 16) *kernel-minor-version*)))
|
||||
|
||||
(defun kernel-dispatcher ()
|
||||
"Kernel Dispatcher Function. This gets called from the main loop in kboot.cpp's KernelCheckAndDispatch"
|
||||
|
||||
;; check if we have a new listener function to run
|
||||
(when *listener-function*
|
||||
;; we do! enable method-set for debug purposes
|
||||
(+! *enable-method-set* 1)
|
||||
|
||||
;; execute and print result
|
||||
(let ((result (*listener-function*)))
|
||||
(format #t "~D~%" result)
|
||||
)
|
||||
(+! *enable-method-set* -1)
|
||||
|
||||
;; clear the pending function.
|
||||
(set! *listener-function* (the (function object) #f))
|
||||
)
|
||||
)
|
||||
@@ -5,3 +5,56 @@
|
||||
;; name in dgo: gstring
|
||||
;; dgos: KERNEL
|
||||
|
||||
;; Note on strings:
|
||||
;; the allocated length does not include an extra byte on the end for the null terminator!
|
||||
|
||||
(defmethod length string ((obj string))
|
||||
; Get the length of a string. Like strlen
|
||||
(let ((str-ptr (-> obj data)))
|
||||
(while (!= 0 (-> str-ptr 0))
|
||||
(set! str-ptr (the (pointer uint8) (&+ str-ptr 1)))
|
||||
)
|
||||
(- (the int str-ptr) (the int (-> obj data)))
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod asize-of string ((obj string))
|
||||
;; get the size in bytes of a string.
|
||||
;; BUG - string should probably be (-> obj type), not that it matters, I don't think
|
||||
;; anybody makes a subclass of string.
|
||||
(+ (-> obj allocated-length) 1 (-> string size))
|
||||
)
|
||||
|
||||
(defun copy-string<-string ((dst string) (src string))
|
||||
"Copy data from one string to another, like strcpy"
|
||||
(let ((dst-ptr (-> dst data))
|
||||
(src-ptr (-> src data))
|
||||
)
|
||||
(while (!= 0 (-> src-ptr))
|
||||
(set! (-> dst-ptr) (-> src-ptr))
|
||||
(&+! dst-ptr 1)
|
||||
(&+! src-ptr 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod new string ((allocation symbol) (type-to-make type) (size int) (other string))
|
||||
"Create a new string of the given size. If other is not #f, copy data from it."
|
||||
(cond
|
||||
(other
|
||||
(let* ((desired-size (max (length other) size))
|
||||
(new-obj (object-new (+ desired-size 1 (-> type-to-make size))))
|
||||
)
|
||||
(set! (-> new-obj allocated-length) size)
|
||||
(copy-string<-string new-obj other)
|
||||
new-obj
|
||||
)
|
||||
)
|
||||
(else
|
||||
(let ((new-obj (object-new (+ 1 size (-> type-to-make size)))))
|
||||
(set! (-> new-obj allocated-length) size)
|
||||
new-obj
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
+6
-8
@@ -1,19 +1,17 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
(test-setup 1.2345 #f)
|
||||
|
||||
(let* ((new-method (-> bfloat methods 0))
|
||||
(print-method (-> bfloat methods 2))
|
||||
(my-float (the bfloat (new-method 'global bfloat)))
|
||||
(let* ((print-method (method bfloat print))
|
||||
(my-float (new 'global 'bfloat))
|
||||
)
|
||||
(set! (-> my-float data) 1.23456)
|
||||
(print-method my-float)
|
||||
(format #t "~%")
|
||||
)
|
||||
|
||||
#|
|
||||
(let ((x 0))
|
||||
(while (< x 9)
|
||||
(format #t "method ~d of ~A is ~A~%" x bfloat (-> bfloat methods x))
|
||||
(format #t "method ~d of ~A is ~A~%" x bfloat (-> bfloat method-table x))
|
||||
(+1! x)
|
||||
)
|
||||
)
|
||||
|#
|
||||
0
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
(start-test "addr-of")
|
||||
(deftype addr-of-test-type (basic)
|
||||
((v1 int32 :offset-assert 4)
|
||||
(arr int32 12 :offset-assert 8)
|
||||
(end uint8 :offset-assert 56)
|
||||
)
|
||||
)
|
||||
|
||||
(let ((temp (new 'global 'addr-of-test-type)))
|
||||
(expect-true (= 8 (&- (&-> temp arr 1) temp)))
|
||||
(expect-true (= 0 (&- (&-> temp v1) temp)))
|
||||
)
|
||||
|
||||
(finish-test)
|
||||
@@ -1,6 +1 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
|
||||
(test-setup 80 #f)
|
||||
|
||||
(+ (align16 1) (align16 (* 3 5)) (align16 (/ 32 2)) (align16 (- -17)))
|
||||
(+ (align16 1) (align16 (* 3 5)) (align16 (/ 32 2)) (align16 (- -17)))
|
||||
@@ -0,0 +1,5 @@
|
||||
(defun type-method-check ((obj type))
|
||||
(align16 (+ 28 (* 4 (-> obj allocated-length))))
|
||||
)
|
||||
|
||||
(type-method-check integer)
|
||||
@@ -1,4 +1,4 @@
|
||||
(test-setup '(a b c d e) #f)
|
||||
|
||||
(format #t "~A~%"
|
||||
(append! (list 'a 'b) (list 'c 'd 'e)))
|
||||
|
||||
0
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
(test-setup 1 #f)
|
||||
(start-test "approx-pi")
|
||||
|
||||
(defun test-approx-pi ((res integer))
|
||||
(let ((rad (* res res))
|
||||
@@ -48,5 +46,4 @@
|
||||
(expect-true (< approx-pi 3.15))
|
||||
)
|
||||
|
||||
;(format #t "~f~%" (test-approx-pi 1000))
|
||||
1
|
||||
(finish-test)
|
||||
@@ -1,7 +1,3 @@
|
||||
(test-setup 'w #f)
|
||||
|
||||
(format #t "~A~%"
|
||||
(cdr (assoc 'e (list (cons 'a 'b) (cons 'e 'w) (cons 'x 'x)))))
|
||||
|
||||
|
||||
(print-type (assoc 'a '()))
|
||||
0
|
||||
@@ -1,4 +1,3 @@
|
||||
(test-setup #f #f)
|
||||
|
||||
(format #t "~A~%"
|
||||
(assoc 'r (list (cons 'a 'b) (cons 'e 'w) (cons 'x 'x))))
|
||||
0
|
||||
@@ -1,4 +1,3 @@
|
||||
(test-setup 'x #f)
|
||||
|
||||
(format #t "~A~%"
|
||||
(cdr (assoce 'r (list (cons 'a 'b) (cons 'e 'w) (cons 'else 'x)))))
|
||||
0
|
||||
@@ -1,4 +1,3 @@
|
||||
(test-setup 'x #f)
|
||||
|
||||
(format #t "~A~%"
|
||||
(cdr (assoce 'r (list (cons 'a 'b) (cons 'r 'x) (cons 'else 'w)))))
|
||||
0
|
||||
+2
-9
@@ -1,11 +1,4 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
(test-setup '#f#t#t#f#t#f#t#t #f)
|
||||
|
||||
;; awful hack to create a bfloat at #x6000000
|
||||
(define-extern hack-bfloat integer)
|
||||
(define hack-bfloat (+ #x6000000 *gtype-basic-offset*))
|
||||
(define-extern hack-bfloat bfloat)
|
||||
(define hack-bfloat (new 'global 'bfloat))
|
||||
|
||||
|
||||
(format #t "~A~A~A~A"
|
||||
@@ -21,4 +14,4 @@
|
||||
(basic-type? #f basic) ;; #t
|
||||
(basic-type? inspect function) ;; #t
|
||||
)
|
||||
|
||||
0
|
||||
@@ -0,0 +1,7 @@
|
||||
(define hack-bfloat (new 'global 'bfloat))
|
||||
|
||||
(set! (-> hack-bfloat type) bfloat)
|
||||
(set! (-> hack-bfloat data) 1.233)
|
||||
|
||||
(format #t "data ~f print ~A type ~A~%" (-> hack-bfloat data) hack-bfloat (-> hack-bfloat type))
|
||||
0
|
||||
@@ -0,0 +1,2 @@
|
||||
(format #t "~A~%" (the binteger -17))
|
||||
0
|
||||
@@ -1,8 +1,4 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
|
||||
(test-setup 'ab #f)
|
||||
|
||||
(let ((my-pair (cons 'a 'b)))
|
||||
(format #t "~A~A~%" (car my-pair) (cdr my-pair))
|
||||
)
|
||||
0
|
||||
@@ -0,0 +1,6 @@
|
||||
(let ((my-pair (cons 'a 'b)))
|
||||
(set! (car my-pair) 'c)
|
||||
(set! (cdr my-pair) 'd)
|
||||
(format #t "~A~%" my-pair)
|
||||
)
|
||||
0
|
||||
+1
-6
@@ -1,7 +1,3 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
(test-setup 4 #f)
|
||||
|
||||
(let ((total 0))
|
||||
(if (true-func)
|
||||
(+! total 1)
|
||||
@@ -23,5 +19,4 @@
|
||||
(+! total 999)
|
||||
)
|
||||
total
|
||||
)
|
||||
|
||||
)
|
||||
@@ -0,0 +1,2 @@
|
||||
(format #t "~A~%" (cons 'a 'b))
|
||||
0
|
||||
@@ -1,5 +1,3 @@
|
||||
(test-setup '((a . b) (e . f)) #f)
|
||||
|
||||
(let ((my-list (list (cons 'a 'b)
|
||||
(cons 'c 'd)
|
||||
(cons 'e 'f)
|
||||
@@ -9,3 +7,4 @@
|
||||
(format #t "~A~%" my-list)
|
||||
(format #t "~A~%" (assoc 'c my-list))
|
||||
)
|
||||
0
|
||||
@@ -1,4 +1,3 @@
|
||||
(test-setup '(a b d e) #f)
|
||||
|
||||
(format #t "~A~%"
|
||||
(delete! 'c (list 'a 'b 'c 'd 'e)))
|
||||
0
|
||||
@@ -1,11 +1,6 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
|
||||
(test-setup 4950 #f)
|
||||
|
||||
(let ((sum 0))
|
||||
(dotimes (i 100 7 8 9 sum)
|
||||
(+! sum i)
|
||||
;;(format #t "iter ~D sum ~D~%" i sum)
|
||||
)
|
||||
)
|
||||
)
|
||||
+23
-17
@@ -1,13 +1,12 @@
|
||||
|
||||
(test-setup 1 #f)
|
||||
(start-test "dynamic-type")
|
||||
|
||||
(deftype test-dynamic-type (basic)
|
||||
(
|
||||
(pad0 int16 :offset 0)
|
||||
(allocated-length int32 :offset 4)
|
||||
(data int32 :dynamic :offset 8)
|
||||
(over1 int32 :offset 8)
|
||||
(over2 int32 :offset 12)
|
||||
(pad0 int16 :offset 4)
|
||||
(allocated-length int32 :offset 8)
|
||||
(data int32 :dynamic :offset 12)
|
||||
(over1 int32 :offset 12)
|
||||
(over2 int32 :offset 16)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -15,12 +14,13 @@
|
||||
(defmethod new test-dynamic-type ((allocation symbol) (type-to-make type) (cnt integer))
|
||||
;"Create a new inline-array. Sets the length, allocated-length to cnt. Uses the mysterious heap-base field
|
||||
;of the type-to-make to determine the element size"
|
||||
(let* ((sz (+ (-> type-to-make asize) (* 4 cnt)))
|
||||
(new-object (object-new sz)))
|
||||
(let* ((sz (+ (-> type-to-make size) (* 4 cnt)))
|
||||
(new-object (object-new (the int sz))))
|
||||
;;(format 0 "create sz ~d at #x~X~%" sz new-object)
|
||||
(unless (zero? new-object)
|
||||
(set! (-> new-object allocated-length) cnt)
|
||||
)
|
||||
|
||||
new-object
|
||||
)
|
||||
)
|
||||
@@ -31,28 +31,34 @@
|
||||
)
|
||||
|
||||
(defmethod asize-of test-dynamic-type ((obj test-dynamic-type))
|
||||
|
||||
;"Get the size in memory of it"
|
||||
(+ (-> obj type asize)
|
||||
(the int (+ (-> obj type size)
|
||||
(* (-> obj allocated-length) 4)
|
||||
)
|
||||
))
|
||||
)
|
||||
|
||||
(define test-dynamic-obj
|
||||
(the test-dynamic-type ((-> test-dynamic-type methods 0) 'global test-dynamic-type 40)))
|
||||
(the test-dynamic-type (new 'global 'test-dynamic-type 40)))
|
||||
|
||||
;(inspect test-dynamic-obj)
|
||||
|
||||
; ;(define test-dynamic-obj (new 'global 'test-dynamic-type 40))
|
||||
|
||||
; ;(inspect test-dynamic-obj)
|
||||
|
||||
(set! (-> test-dynamic-obj data 0) 12)
|
||||
(set! (-> test-dynamic-obj data 1) 20)
|
||||
;(inspect test-dynamic-obj)
|
||||
; ;(inspect test-dynamic-obj)
|
||||
|
||||
; (format #t "should be same (~d ~d) (~d ~d)~%" (-> test-dynamic-obj data 0) (-> test-dynamic-obj over1)
|
||||
; (-> test-dynamic-obj data 1) (-> test-dynamic-obj over2))
|
||||
; ; (format #t "should be same (~d ~d) (~d ~d)~%" (-> test-dynamic-obj data 0) (-> test-dynamic-obj over1)
|
||||
; ; (-> test-dynamic-obj data 1) (-> test-dynamic-obj over2))
|
||||
|
||||
(expect-true (= (-> test-dynamic-obj data 0) (-> test-dynamic-obj over1)))
|
||||
(expect-true (= (-> test-dynamic-obj data 1) (-> test-dynamic-obj over2)))
|
||||
|
||||
(set! (-> test-dynamic-obj pad0) 0)
|
||||
(expect-true (= (-> test-dynamic-obj type) test-dynamic-type))
|
||||
|
||||
(expect-true (= (asize-of test-dynamic-obj) 180))
|
||||
1
|
||||
|
||||
(finish-test)
|
||||
@@ -0,0 +1,2 @@
|
||||
(format #t "~A~%" '())
|
||||
0
|
||||
+2
-7
@@ -1,12 +1,8 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
(test-setup "test pass!" #f)
|
||||
|
||||
(let ((test-result "test fail!"))
|
||||
|
||||
;; first, do one where we get something
|
||||
(if (eq?
|
||||
(-> structure methods 1)
|
||||
(-> structure method-table 1)
|
||||
(find-parent-method bfloat 1)
|
||||
)
|
||||
(set! test-result "test pass!")
|
||||
@@ -23,5 +19,4 @@
|
||||
|
||||
(print test-result)
|
||||
)
|
||||
|
||||
|
||||
0
|
||||
@@ -1,4 +1,3 @@
|
||||
(define-extern _format function)
|
||||
(define format _format)
|
||||
|
||||
(format #t "test ~D ~D ~D ~D ~D ~D~%" 1 2 3 4 5 6)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
(let* ((base (the int integer))
|
||||
(field (the int (-> integer method-table)))
|
||||
(offset (- field base)))
|
||||
;;(format #t "offset of methods table is ~d~%" offset)
|
||||
offset
|
||||
)
|
||||
@@ -1,8 +1,7 @@
|
||||
(test-setup '((c . w) (a . b) (e . f)) #f)
|
||||
|
||||
(let ((alist (list (cons 'a 'b)
|
||||
(cons 'c 'd)
|
||||
(cons 'e 'f))))
|
||||
(set! alist (insert-cons! (cons 'c 'w) alist))
|
||||
(format #t "~A~%" alist)
|
||||
)
|
||||
0
|
||||
@@ -1,6 +1,4 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
(test-setup 'd #f)
|
||||
(format #t "~A~%"
|
||||
(car (last (list 'a 'b 'c 'd)))
|
||||
)
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
(format #t "~A~%" (list 'a 'b 'c 'd))
|
||||
@@ -1,4 +1,3 @@
|
||||
(test-setup '(c d) #f)
|
||||
|
||||
(format #t "~A~%"
|
||||
(member 'c (list 'a 'b 'c 'd)))
|
||||
0
|
||||
@@ -1,5 +1,3 @@
|
||||
(test-setup #f #f)
|
||||
|
||||
(format #t "~A~%"
|
||||
(member 1234 (list 'a 'b 'c 'd))
|
||||
)
|
||||
)
|
||||
@@ -1,8 +1,3 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
;; add two constants together.
|
||||
(test-setup 13 #f)
|
||||
|
||||
(let* ((base-addr #x6000000)
|
||||
(offset #x123)
|
||||
(ptr-int32 (the (pointer int32) base-addr))
|
||||
@@ -14,5 +9,3 @@
|
||||
(-> ptr-int16 3)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
;; add two constants together.
|
||||
(test-setup #x0b #f)
|
||||
|
||||
(let* ((base-addr #x6000000)
|
||||
(word-cnt 23)
|
||||
(base (the (pointer int32) base-addr))
|
||||
@@ -12,14 +7,12 @@
|
||||
)
|
||||
|
||||
(if (!= dst base)
|
||||
(format #t "test failed, bad base returned!~%")
|
||||
)
|
||||
|
||||
(format #t "test failed, bad base returned!~%")
|
||||
)
|
||||
|
||||
(if (!= 0 (-> last-byte 1))
|
||||
(format #t "set too many bytes!~%")
|
||||
)
|
||||
|
||||
(format #t "set too many bytes!~%")
|
||||
)
|
||||
|
||||
(-> last-byte 0)
|
||||
)
|
||||
|
||||
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
(format #t "~A~A~%" (eq? (-> process method-table 2) (method process print))
|
||||
(eq? (-> string method-table 3) (method "test" inspect))
|
||||
)
|
||||
0
|
||||
+1
-5
@@ -1,8 +1,3 @@
|
||||
;-*-Scheme-*-
|
||||
|
||||
|
||||
(test-setup 'efgh #f)
|
||||
|
||||
(let ((my-pair (cons (cons 'a 'b) (cons 'c 'd))))
|
||||
(set! (car (car my-pair)) 'e)
|
||||
(set! (car (cdr my-pair)) 'f)
|
||||
@@ -11,3 +6,4 @@
|
||||
(format #t "~A~A~A~A~%" (car (car my-pair)) (car (cdr my-pair)) (cdr (car my-pair)) (cdr (cdr my-pair)))
|
||||
(format #t "~A~%" my-pair)
|
||||
)
|
||||
0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user