Merge remote-tracking branch 'water111/master' into windows-nodeci

This commit is contained in:
Tyler Wilding
2020-09-03 20:16:28 -04:00
108 changed files with 8301 additions and 2203 deletions
+26
View File
@@ -0,0 +1,26 @@
name: Linux Workflow
on: [push]
jobs:
build:
name: Build and Test Project (Linux)
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Get Package Dependencies
run: sudo apt install gcc make cmake build-essential g++ nasm clang-format
- name: Initialize Submodules
run: git submodule update --init --recursive
- name: Build Project with CMake
run: |
mkdir build
cd build
cmake ..
make -j
- name: Test Project with gTest
run: ./test.sh
- name: Check Clang-Formatting
run: |
chmod +x ./third-party/run-clang-format/run-clang-format.py
./third-party/run-clang-format/run-clang-format.py -r common decompiler game goalc test --color always
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "third-party/googletest"]
path = third-party/googletest
url = https://github.com/google/googletest.git
url = https://github.com/google/googletest.git
+1
View File
@@ -1,4 +1,5 @@
# Jak Project
![Linux Workflow](https://github.com/water111/jak-project/workflows/Linux%20Workflow/badge.svg?branch=master)
## Table of Contents
+15 -7
View File
@@ -1,16 +1,24 @@
#ifndef JAK_GOAL_CONSTANTS_H
#define JAK_GOAL_CONSTANTS_H
#include "common_types.h"
constexpr s32 BINTEGER_OFFSET = 0;
constexpr s32 PAIR_OFFSET = 2;
constexpr int POINTER_SIZE = 4;
constexpr int BASIC_OFFSET = 4;
constexpr int STRUCTURE_ALIGNMENT = 16;
enum class RegKind {
GPR_64,
FLOAT,
INT_128,
FLOAT_4X,
INVALID
};
enum class RegKind { GPR_64, FLOAT, INT_128, FLOAT_4X, INVALID };
constexpr u32 GOAL_NEW_METHOD = 0; // method ID of GOAL new
constexpr u32 GOAL_DEL_METHOD = 1; // method ID of GOAL delete
constexpr u32 GOAL_PRINT_METHOD = 2; // method ID of GOAL print
constexpr u32 GOAL_INSPECT_METHOD = 3; // method ID of GOAL inspect
constexpr u32 GOAL_LENGTH_METHOD = 4; // method ID of GOAL length
constexpr u32 GOAL_ASIZE_METHOD = 5; // method ID of GOAL size
constexpr u32 GOAL_COPY_METHOD = 6; // method ID of GOAL copy
constexpr u32 GOAL_RELOC_METHOD = 7; // method ID of GOAL relocate
constexpr u32 GOAL_MEMUSAGE_METHOD = 8; // method ID of GOAL mem-usage
#endif // JAK_GOAL_CONSTANTS_H
+62 -61
View File
@@ -6,75 +6,76 @@
#ifndef JAK1_SYMBOLS_H
#define JAK1_SYMBOLS_H
constexpr int FIX_SYM_EMPTY_CAR = -0xc;
constexpr int FIX_SYM_EMPTY_CAR = -0xc;
constexpr int FIX_SYM_EMPTY_PAIR = -0xa;
constexpr int FIX_SYM_EMPTY_CDR = -0x8;
constexpr int FIX_SYM_FALSE = 0x0; // GOAL boolean #f (note that this is equal to the $s7 register)
constexpr int FIX_SYM_TRUE = 0x8; // GOAL boolean #t
constexpr int FIX_SYM_EMPTY_CDR = -0x8;
constexpr int FIX_SYM_FALSE = 0x0; // GOAL boolean #f (note that this is equal to the $s7 register)
constexpr int FIX_SYM_TRUE = 0x8; // GOAL boolean #t
// types
constexpr int FIX_SYM_FUNCTION_TYPE = 0x10; // GOAL type of function
constexpr int FIX_SYM_BASIC_TYPE = 0x18; // GOAL structure type with type tag
constexpr int FIX_SYM_STRING_TYPE = 0x20; // GOAL string type (gstring)
constexpr int FIX_SYM_SYMBOL_TYPE = 0x28; // GOAL symbol type
constexpr int FIX_SYM_TYPE_TYPE = 0x30; // GOAL type of type
constexpr int FIX_SYM_OBJECT_TYPE = 0x38; // GOAL parent type of all types
constexpr int FIX_SYM_LINK_BLOCK = 0x40; // GOAL type of link-block (used by linker, but seems to be unused by GOAL)
constexpr int FIX_SYM_INTEGER_TYPE = 0x48; // GOAL integer parent type, assumes unboxed
constexpr int FIX_SYM_SINTEGER_TYPE = 0x50; // GOAL signed integer parent type, assumes unboxed
constexpr int FIX_SYM_UINTEGER_TYPE = 0x58; // GOAL unsinged integer parent type, assumes unboxed
constexpr int FIX_SYM_BINTEGER_TYPE = 0x60; // GOAL "boxed integer" type
constexpr int FIX_SYM_INT8_TYPE = 0x68; // GOAL 8-bit signed integer
constexpr int FIX_SYM_INT16_TYPE = 0x70; // ...
constexpr int FIX_SYM_INT32_TYPE = 0x78; // ...
constexpr int FIX_SYM_INT64_TYPE = 0x80; // ...
constexpr int FIX_SYM_INT128_TYPE = 0x88; // GOAL 128-bit integer type, behaves strangely
constexpr int FIX_SYM_UINT8_TYPE = 0x90; // GOAL 8-bit unsigned integer
constexpr int FIX_SYM_UINT16_TYPE = 0x98; // ...
constexpr int FIX_SYM_UINT32_TYPE = 0xA0; // ...
constexpr int FIX_SYM_UINT64_TYPE = 0xA8; // ...
constexpr int FIX_SYM_UINT128_TYPE = 0xB0; // ...
constexpr int FIX_SYM_FLOAT_TYPE = 0xB8; // GOAL 32-bit floating point type
constexpr int FIX_SYM_PROCESS_TREE_TYPE = 0xC0; // GOAL process-tree type. Used in the gkernel
constexpr int FIX_SYM_PROCESS_TYPE = 0xC8; // GOAL process type
constexpr int FIX_SYM_THREAD_TYPE = 0xD0; // GOAL thread type
constexpr int FIX_SYM_STRUCTURE_TYPE = 0xD8; // GOAL structure type. Any type with fields
constexpr int FIX_SYM_PAIR_TYPE = 0xE0; // GOAL pair type
constexpr int FIX_SYM_POINTER_TYPE = 0xE8; // GOAL pointer type (32-bit)
constexpr int FIX_SYM_NUMBER_TYPE = 0xF0; // GOAL number type (parent of integer/float types)
constexpr int FIX_SYM_ARRAY_TYPE = 0xF8; // GOAL array type
constexpr int FIX_SYM_VU_FUNCTION_TYPE = 0x100; // GOAL vu-function type
constexpr int FIX_SYM_CONNECTABLE_TYPE = 0x108; // GOAL connectable
constexpr int FIX_SYM_STACK_FRAME_TYPE = 0x110; // GOAL stack-frame
constexpr int FIX_SYM_FILE_STREAM_TYPE = 0x118; // GOAL file-stream
constexpr int FIX_SYM_KHEAP = 0x120; // GOAL kheap
constexpr int FIX_SYM_FUNCTION_TYPE = 0x10; // GOAL type of function
constexpr int FIX_SYM_BASIC_TYPE = 0x18; // GOAL structure type with type tag
constexpr int FIX_SYM_STRING_TYPE = 0x20; // GOAL string type (gstring)
constexpr int FIX_SYM_SYMBOL_TYPE = 0x28; // GOAL symbol type
constexpr int FIX_SYM_TYPE_TYPE = 0x30; // GOAL type of type
constexpr int FIX_SYM_OBJECT_TYPE = 0x38; // GOAL parent type of all types
constexpr int FIX_SYM_LINK_BLOCK =
0x40; // GOAL type of link-block (used by linker, but seems to be unused by GOAL)
constexpr int FIX_SYM_INTEGER_TYPE = 0x48; // GOAL integer parent type, assumes unboxed
constexpr int FIX_SYM_SINTEGER_TYPE = 0x50; // GOAL signed integer parent type, assumes unboxed
constexpr int FIX_SYM_UINTEGER_TYPE = 0x58; // GOAL unsinged integer parent type, assumes unboxed
constexpr int FIX_SYM_BINTEGER_TYPE = 0x60; // GOAL "boxed integer" type
constexpr int FIX_SYM_INT8_TYPE = 0x68; // GOAL 8-bit signed integer
constexpr int FIX_SYM_INT16_TYPE = 0x70; // ...
constexpr int FIX_SYM_INT32_TYPE = 0x78; // ...
constexpr int FIX_SYM_INT64_TYPE = 0x80; // ...
constexpr int FIX_SYM_INT128_TYPE = 0x88; // GOAL 128-bit integer type, behaves strangely
constexpr int FIX_SYM_UINT8_TYPE = 0x90; // GOAL 8-bit unsigned integer
constexpr int FIX_SYM_UINT16_TYPE = 0x98; // ...
constexpr int FIX_SYM_UINT32_TYPE = 0xA0; // ...
constexpr int FIX_SYM_UINT64_TYPE = 0xA8; // ...
constexpr int FIX_SYM_UINT128_TYPE = 0xB0; // ...
constexpr int FIX_SYM_FLOAT_TYPE = 0xB8; // GOAL 32-bit floating point type
constexpr int FIX_SYM_PROCESS_TREE_TYPE = 0xC0; // GOAL process-tree type. Used in the gkernel
constexpr int FIX_SYM_PROCESS_TYPE = 0xC8; // GOAL process type
constexpr int FIX_SYM_THREAD_TYPE = 0xD0; // GOAL thread type
constexpr int FIX_SYM_STRUCTURE_TYPE = 0xD8; // GOAL structure type. Any type with fields
constexpr int FIX_SYM_PAIR_TYPE = 0xE0; // GOAL pair type
constexpr int FIX_SYM_POINTER_TYPE = 0xE8; // GOAL pointer type (32-bit)
constexpr int FIX_SYM_NUMBER_TYPE = 0xF0; // GOAL number type (parent of integer/float types)
constexpr int FIX_SYM_ARRAY_TYPE = 0xF8; // GOAL array type
constexpr int FIX_SYM_VU_FUNCTION_TYPE = 0x100; // GOAL vu-function type
constexpr int FIX_SYM_CONNECTABLE_TYPE = 0x108; // GOAL connectable
constexpr int FIX_SYM_STACK_FRAME_TYPE = 0x110; // GOAL stack-frame
constexpr int FIX_SYM_FILE_STREAM_TYPE = 0x118; // GOAL file-stream
constexpr int FIX_SYM_KHEAP = 0x120; // GOAL kheap
// GOAL functions
constexpr int FIX_SYM_NOTHING_FUNC = 0x128; // GOAL nothing-func (does nothing)
constexpr int FIX_SYM_DEL_BASIC_FUNC = 0x130; // GOAL delete-basic function
constexpr int FIX_SYM_NOTHING_FUNC = 0x128; // GOAL nothing-func (does nothing)
constexpr int FIX_SYM_DEL_BASIC_FUNC = 0x130; // GOAL delete-basic function
// GOAL allocation symbols (?)
constexpr int FIX_SYM_STATIC = 0x138; // GOAL 'static
constexpr int FIX_SYM_GLOBAL_HEAP = 0x140; // GOAL 'global
constexpr int FIX_SYM_DEBUG_HEAP = 0x148; // GOAL 'debug
constexpr int FIX_SYM_LOADING_LEVEL = 0x150; // ??
constexpr int FIX_SYM_LOADING_PACKAGE = 0x158; // ??
constexpr int FIX_SYM_PROCESS_LEVEL_HEAP = 0x160; // ??
constexpr int FIX_SYM_STACK = 0x168; // GOAL 'stack
constexpr int FIX_SYM_SCRATCH = 0x170; // GOAL 'scratch
constexpr int FIX_SYM_STATIC = 0x138; // GOAL 'static
constexpr int FIX_SYM_GLOBAL_HEAP = 0x140; // GOAL 'global
constexpr int FIX_SYM_DEBUG_HEAP = 0x148; // GOAL 'debug
constexpr int FIX_SYM_LOADING_LEVEL = 0x150; // ??
constexpr int FIX_SYM_LOADING_PACKAGE = 0x158; // ??
constexpr int FIX_SYM_PROCESS_LEVEL_HEAP = 0x160; // ??
constexpr int FIX_SYM_STACK = 0x168; // GOAL 'stack
constexpr int FIX_SYM_SCRATCH = 0x170; // GOAL 'scratch
// GOAL random stuff
constexpr int FIX_SYM_SCRATCH_TOP = 0x178; // GOAL *scratch-top*
constexpr int FIX_SYM_ZERO_FUNC = 0x180; // GOAL zero-func (returns 0x0 in $v0 register)
constexpr int FIX_SYM_ASIZE_OF_BASIC_FUNC = 0x188; // GOAL asize-of-basic function
constexpr int FIX_SYM_COPY_BASIC_FUNC = 0x190; // GOAL copy-basic function
constexpr int FIX_SYM_LEVEL = 0x198; // ??
constexpr int FIX_SYM_ART_GROUP = 0x1a0; // ??
constexpr int FIX_SYM_TX_PAGE_DIR = 0x1a8; // ??
constexpr int FIX_SYM_TX_PAGE = 0x1b0; // ??
constexpr int FIX_SYM_SOUND = 0x1b8; // ??
constexpr int FIX_SYM_DGO = 0x1c0; // ??
constexpr int FIX_SYM_TOP_LEVEL = 0x1c8; // ??
constexpr int FIX_FIXED_SYM_END_OFFSET = 0x1d0;
constexpr int FIX_SYM_SCRATCH_TOP = 0x178; // GOAL *scratch-top*
constexpr int FIX_SYM_ZERO_FUNC = 0x180; // GOAL zero-func (returns 0x0 in $v0 register)
constexpr int FIX_SYM_ASIZE_OF_BASIC_FUNC = 0x188; // GOAL asize-of-basic function
constexpr int FIX_SYM_COPY_BASIC_FUNC = 0x190; // GOAL copy-basic function
constexpr int FIX_SYM_LEVEL = 0x198; // ??
constexpr int FIX_SYM_ART_GROUP = 0x1a0; // ??
constexpr int FIX_SYM_TX_PAGE_DIR = 0x1a8; // ??
constexpr int FIX_SYM_TX_PAGE = 0x1b0; // ??
constexpr int FIX_SYM_SOUND = 0x1b8; // ??
constexpr int FIX_SYM_DGO = 0x1c0; // ??
constexpr int FIX_SYM_TOP_LEVEL = 0x1c8; // ??
constexpr int FIX_FIXED_SYM_END_OFFSET = 0x1d0;
#endif // JAK1_SYMBOLS_H
+31 -25
View File
@@ -108,6 +108,11 @@ std::string Type::get_name() const {
}
std::string Type::get_runtime_name() const {
if (!m_allow_in_runtime) {
fmt::print("[TypeSystem] Tried to use type {} as a runtime type, which is not allowed.\n",
get_name());
throw std::runtime_error("get_runtime_name");
}
return m_runtime_name;
}
@@ -137,7 +142,7 @@ bool Type::is_equal(const Type& other) const {
* parents.
*/
bool Type::has_parent() const {
return m_parent != "object" && !m_parent.empty();
return m_name != "object" && !m_parent.empty();
}
/*!
@@ -218,52 +223,53 @@ std::string Type::print_method_info() const {
}
/////////////
// NoneType
// NullType
/////////////
// Special Type representing nothing.
// it's an error to try to do anything with None.
// Special Type for both "none" and "_type_" types
// it's an error to try to do anything with Null.
NoneType::NoneType() : Type("", "none", false) {}
NullType::NullType(std::string name) : Type("", std::move(name), false) {}
bool NoneType::is_reference() const {
throw std::runtime_error("is_reference called on NoneType");
bool NullType::is_reference() const {
throw std::runtime_error("is_reference called on NullType");
}
int NoneType::get_load_size() const {
throw std::runtime_error("get_load_size called on NoneType");
int NullType::get_load_size() const {
throw std::runtime_error("get_load_size called on NullType");
}
bool NoneType::get_load_signed() const {
throw std::runtime_error("get_load_size called on NoneType");
bool NullType::get_load_signed() const {
throw std::runtime_error("get_load_size called on NullType");
}
int NoneType::get_size_in_memory() const {
throw std::runtime_error("get_size_in_memory called on NoneType");
int NullType::get_size_in_memory() const {
throw std::runtime_error("get_size_in_memory called on NullType");
}
RegKind NoneType::get_preferred_reg_kind() const {
throw std::runtime_error("get_preferred_reg_kind called on NoneType");
RegKind NullType::get_preferred_reg_kind() const {
throw std::runtime_error("get_preferred_reg_kind called on NullType");
}
int NoneType::get_offset() const {
int NullType::get_offset() const {
throw std::runtime_error("get_offset called on NoneType");
}
int NoneType::get_in_memory_alignment() const {
throw std::runtime_error("get_in_memory_alignment called on NoneType");
int NullType::get_in_memory_alignment() const {
throw std::runtime_error("get_in_memory_alignment called on NullType");
}
int NoneType::get_inline_array_alignment() const {
throw std::runtime_error("get_inline_array_alignment called on NoneType");
int NullType::get_inline_array_alignment() const {
throw std::runtime_error("get_inline_array_alignment called on NullType");
}
std::string NoneType::print() const {
return "none";
std::string NullType::print() const {
return m_name;
}
bool NoneType::operator==(const Type& other) const {
// there should be only one none type, so this is safe.
bool NullType::operator==(const Type& other) const {
// any redefinition by the user should be invalid, so this will always return false unless
// you're calling it on the same object.
return this == &other;
}
@@ -484,7 +490,7 @@ void StructureType::override_size_in_memory(int size) {
}
int StructureType::get_offset() const {
return 0;
return m_offset;
}
int StructureType::get_in_memory_alignment() const {
+9 -3
View File
@@ -68,6 +68,8 @@ class Type {
const MethodInfo& add_new_method(const MethodInfo& info);
std::string print_method_info() const;
void disallow_in_runtime() { m_allow_in_runtime = false; }
virtual ~Type() = default;
protected:
@@ -79,6 +81,7 @@ class Type {
std::string m_parent; // the parent type (is empty for none and object)
std::string m_name;
bool m_allow_in_runtime = true;
std::string m_runtime_name;
bool m_is_boxed = false; // does this have runtime type information?
};
@@ -87,9 +90,9 @@ class Type {
* Used only for "none" - this is a type that the compiler can use for "this has no value".
* Attempting to do anything with a NoneType is an error.
*/
class NoneType : public Type {
class NullType : public Type {
public:
NoneType();
NullType(std::string name);
bool is_reference() const override;
int get_load_size() const override;
bool get_load_signed() const override;
@@ -100,7 +103,7 @@ class NoneType : public Type {
int get_in_memory_alignment() const override;
std::string print() const override;
bool operator==(const Type& other) const override;
~NoneType() = default;
~NullType() = default;
};
/*!
@@ -217,10 +220,12 @@ class StructureType : public ReferenceType {
int get_in_memory_alignment() const override;
int get_inline_array_alignment() const override;
bool lookup_field(const std::string& name, Field* out);
bool is_dynamic() const { return m_dynamic; }
~StructureType() = default;
protected:
friend class TypeSystem;
void override_offset(int offset) { m_offset = offset; }
void override_size_in_memory(
int size); // only to be used for setting up weird types like "structure"
void add_field(const Field& f, int new_size_in_mem) {
@@ -234,6 +239,7 @@ class StructureType : public ReferenceType {
bool m_dynamic = false;
int m_size_in_mem = 0;
bool m_pack = false;
int m_offset = 0;
};
class BasicType : public StructureType {
+9
View File
@@ -34,4 +34,13 @@ bool TypeSpec::operator==(const TypeSpec& other) const {
}
return true;
}
TypeSpec TypeSpec::substitute_for_method_call(const std::string& method_type) const {
TypeSpec result;
result.m_type = (m_type == "_type_") ? method_type : m_type;
for (const auto& x : m_arguments) {
result.m_arguments.push_back(x.substitute_for_method_call(method_type));
}
return result;
}
+6
View File
@@ -33,12 +33,18 @@ class TypeSpec {
void add_arg(const TypeSpec& ts) { m_arguments.push_back(ts); }
const std::string base_type() const { return m_type; }
bool has_single_arg() const { return m_arguments.size() == 1; }
const TypeSpec& get_single_arg() const {
assert(m_arguments.size() == 1);
return m_arguments.front();
}
TypeSpec substitute_for_method_call(const std::string& method_type) const;
private:
friend class TypeSystem;
std::string m_type;
std::vector<TypeSpec> m_arguments;
};
+260 -56
View File
@@ -6,8 +6,9 @@
#include <stdexcept>
TypeSystem::TypeSystem() {
// the "none" type is included by default.
add_type("none", std::make_unique<NoneType>());
// 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_"));
}
/*!
@@ -40,7 +41,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") {
if (name != "object" && name != "none" && name != "_type_") {
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());
@@ -84,11 +85,24 @@ std::string TypeSystem::get_runtime_type(const TypeSpec& ts) {
DerefInfo TypeSystem::get_deref_info(const TypeSpec& ts) {
DerefInfo info;
if (!ts.has_single_arg()) {
// not enough info.
info.can_deref = false;
return info;
}
// default to GPR
info.reg = RegKind::GPR_64;
info.mem_deref = true;
if (ts.base_type() == "inline-array") {
auto result_type = lookup_type(ts.get_single_arg());
auto result_structure_type = dynamic_cast<StructureType*>(result_type);
if (!result_structure_type || result_structure_type->is_dynamic()) {
info.can_deref = false;
return info;
}
// it's an inline array of structures. We can "dereference". But really we don't do a memory
// dereference, we just add stride*idx to the pointer.
info.can_deref = true; // deref operators should work...
@@ -96,7 +110,6 @@ DerefInfo TypeSystem::get_deref_info(const TypeSpec& ts) {
info.result_type = ts.get_single_arg(); // what we're an inline-array of
info.sign_extend = false; // not applicable anyway
auto result_type = lookup_type(info.result_type);
if (result_type->is_reference()) {
info.stride =
align(result_type->get_size_in_memory(), result_type->get_inline_array_alignment());
@@ -112,11 +125,13 @@ DerefInfo TypeSystem::get_deref_info(const TypeSpec& ts) {
// in memory, an array of pointers
info.stride = POINTER_SIZE;
info.sign_extend = false;
info.load_size = POINTER_SIZE;
} else {
// an array of values, which should be loaded in the correct way to the correct register
info.stride = result_type->get_size_in_memory();
info.sign_extend = result_type->get_load_signed();
info.reg = result_type->get_preferred_reg_kind();
info.load_size = result_type->get_load_size();
assert(result_type->get_size_in_memory() == result_type->get_load_size());
}
} else {
@@ -188,7 +203,7 @@ TypeSpec TypeSystem::make_inline_array_typespec(const TypeSpec& type) {
* possible, don't store a Type* and store a TypeSpec instead. The TypeSpec can then be used with
* lookup_type to find the most up-to-date type information.
*/
Type* TypeSystem::lookup_type(const std::string& name) {
Type* TypeSystem::lookup_type(const std::string& name) const {
auto kv = m_types.find(name);
if (kv != m_types.end()) {
return kv->second.get();
@@ -209,7 +224,7 @@ Type* TypeSystem::lookup_type(const std::string& name) {
* possible, don't store a Type* and store a TypeSpec instead. The TypeSpec can then be used with
* lookup_type to find the most up-to-date type information.
*/
Type* TypeSystem::lookup_type(const TypeSpec& ts) {
Type* TypeSystem::lookup_type(const TypeSpec& ts) const {
return lookup_type(ts.base_type());
}
@@ -488,7 +503,7 @@ int TypeSystem::add_field_to_type(StructureType* type,
* Add types which are built-in to GOAL.
*/
void TypeSystem::add_builtin_types() {
// some of the basic types having confusing circular dependencies, so this is done manually.
// some of the basic types have confusing circular dependencies, so this is done manually.
// there are no inlined things so its ok to do some things out of order because the actual size
// doesn't really matter.
@@ -506,48 +521,53 @@ void TypeSystem::add_builtin_types() {
auto link_block_type = add_builtin_basic("basic", "link-block");
auto kheap_type = add_builtin_structure("structure", "kheap");
auto array_type = add_builtin_basic("basic", "array");
auto pair_type = add_builtin_structure("object", "pair");
auto pair_type = add_builtin_structure("object", "pair", true);
auto process_tree_type = add_builtin_basic("basic", "process-tree");
auto process_type = add_builtin_basic("process-tree", "process");
auto thread_type = add_builtin_basic("basic", "thread");
auto connectable_type = add_builtin_structure("structure", "connectable");
auto stack_frame_type = add_builtin_basic("basic", "stack-frame");
auto file_stream_type = add_builtin_basic("basic", "file-stream");
auto pointer_type = add_builtin_value_type("object", "pointer", 4);
auto number_type = add_builtin_value_type("object", "number", 8); // sign extend?
auto float_type = add_builtin_value_type("number", "float", 4, false, false, RegKind::FLOAT);
auto integer_type = add_builtin_value_type("number", "integer", 8, false, false); // sign extend?
auto binteger_type =
add_builtin_value_type("integer", "binteger", 8, true, false); // sign extend?
auto sinteger_type = add_builtin_value_type("integer", "sinteger", 8, false, true);
auto int8_type = add_builtin_value_type("sinteger", "int8", 1, false, true);
auto int16_type = add_builtin_value_type("sinteger", "int16", 2, false, true);
auto int32_type = add_builtin_value_type("sinteger", "int32", 4, false, true);
auto int64_type = add_builtin_value_type("sinteger", "int64", 8, false, true);
auto int128_type =
add_builtin_value_type("sinteger", "int128", 16, false, true, RegKind::INT_128);
auto uinteger_type = add_builtin_value_type("integer", "uinteger", 8);
auto uint8_type = add_builtin_value_type("uinteger", "uint8", 1);
auto uint16_type = add_builtin_value_type("uinteger", "uint16", 2);
auto uint32_type = add_builtin_value_type("uinteger", "uint32", 4);
auto uint64_type = add_builtin_value_type("uinteger", "uint64", 81);
auto uint128_type =
add_builtin_value_type("uinteger", "uint128", 16, false, false, RegKind::INT_128);
add_builtin_value_type("object", "pointer", 4);
auto inline_array_type = add_builtin_value_type("object", "inline-array", 4);
inline_array_type->set_runtime_type("pointer");
add_builtin_value_type("object", "number", 8); // sign extend?
add_builtin_value_type("number", "float", 4, false, false, RegKind::FLOAT);
add_builtin_value_type("number", "integer", 8, false, false); // sign extend?
add_builtin_value_type("integer", "binteger", 8, true, false); // sign extend?
add_builtin_value_type("integer", "sinteger", 8, false, true);
add_builtin_value_type("sinteger", "int8", 1, false, true);
add_builtin_value_type("sinteger", "int16", 2, false, true);
add_builtin_value_type("sinteger", "int32", 4, false, true);
add_builtin_value_type("sinteger", "int64", 8, false, true);
add_builtin_value_type("sinteger", "int128", 16, false, true, RegKind::INT_128);
add_builtin_value_type("integer", "uinteger", 8);
add_builtin_value_type("uinteger", "uint8", 1);
add_builtin_value_type("uinteger", "uint16", 2);
add_builtin_value_type("uinteger", "uint32", 4);
add_builtin_value_type("uinteger", "uint64", 81);
add_builtin_value_type("uinteger", "uint128", 16, false, false, RegKind::INT_128);
auto int_type = add_builtin_value_type("integer", "int", 8, false, true);
int_type->disallow_in_runtime();
auto uint_type = add_builtin_value_type("uinteger", "uint", 8, false, false);
uint_type->disallow_in_runtime();
// Methods and Fields
// OBJECT
add_method(obj_type, "new", make_function_typespec({"symbol", "type", "int32"}, "object"));
add_method(obj_type, "delete", make_function_typespec({"object"}, "none"));
add_method(obj_type, "print", make_function_typespec({"object"}, "object"));
add_method(obj_type, "inspect", make_function_typespec({"object"}, "object"));
add_method(obj_type, "new", make_function_typespec({"symbol", "type", "int32"}, "_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({"object"}, "int32")); // todo - this integer type?
add_method(obj_type, "asize-of", make_function_typespec({"object"}, "int32"));
add_method(obj_type, "copy", make_function_typespec({"object", "symbol"}, "object"));
add_method(obj_type, "relocate", make_function_typespec({"object", "int32"}, "object"));
make_function_typespec({"_type_"}, "int32")); // todo - this integer type?
add_method(obj_type, "asize-of", make_function_typespec({"_type_"}, "int32"));
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",
make_function_typespec({"object"}, "int32")); // todo - this is a guess.
make_function_typespec({"_type_"}, "int32")); // todo - this is a guess.
// STRUCTURE
// structure new doesn't support dynamic sizing, which is kinda weird - it grabs the size from
@@ -591,6 +611,38 @@ void TypeSystem::add_builtin_types() {
// VU FUNCTION
// don't inherit
add_field_to_type(vu_function_type, "length", make_typespec("int32")); // todo integer type
add_field_to_type(vu_function_type, "origin", make_typespec("pointer")); // todo sign extend?
add_field_to_type(vu_function_type, "qlength", make_typespec("int32")); // todo integer type
// link block
builtin_structure_inherit(link_block_type);
add_field_to_type(link_block_type, "allocated-length",
make_typespec("int32")); // todo integer type
add_field_to_type(link_block_type, "version", make_typespec("int32")); // todo integer type
// there's probably some dynamically sized stuff after this...
// kheap
add_field_to_type(kheap_type, "base", make_typespec("pointer"));
add_field_to_type(kheap_type, "top", make_typespec("pointer"));
add_field_to_type(kheap_type, "current", make_typespec("pointer"));
add_field_to_type(kheap_type, "top-base", make_typespec("pointer"));
// todo
(void)array_type;
// pair
pair_type->override_offset(2);
add_field_to_type(pair_type, "car", make_typespec("object"));
add_field_to_type(pair_type, "cdr", make_typespec("object"));
// todo, with kernel
(void)process_tree_type;
(void)process_type;
(void)thread_type;
(void)connectable_type;
(void)stack_frame_type;
(void)file_stream_type;
}
/*!
@@ -625,23 +677,6 @@ int TypeSystem::get_next_method_id(Type* type) {
}
}
/*!
* For debugging, todo remove.
*/
int TypeSystem::manual_add_field_to_type(StructureType* type,
const std::string& field_name,
const TypeSpec& field_type,
int offset,
int size,
int alignment) {
Field field(field_name, field_type);
field.set_alignment(alignment);
field.set_offset(offset);
int new_size = type->get_size_in_memory() + size;
type->add_field(field, new_size);
return offset;
}
/*!
* Lookup a field of a type by name
*/
@@ -725,8 +760,9 @@ int TypeSystem::get_size_in_type(const Field& field) {
* things in the wrong order.
*/
StructureType* TypeSystem::add_builtin_structure(const std::string& parent,
const std::string& type_name) {
add_type(type_name, std::make_unique<StructureType>(parent, type_name));
const std::string& type_name,
bool boxed) {
add_type(type_name, std::make_unique<StructureType>(parent, type_name, boxed));
return get_type_of_type<StructureType>(type_name);
}
@@ -759,4 +795,172 @@ ValueType* TypeSystem::add_builtin_value_type(const std::string& parent,
*/
void TypeSystem::builtin_structure_inherit(StructureType* st) {
st->inherit(get_type_of_type<StructureType>(st->get_parent()));
}
/*!
* Main compile-time type check!
* @param expected - the expected type
* @param actual - the actual type (can be more specific)
* @param error_source_name - optional, can provide a name for where the error comes from
* @param print_on_error - print a message explaining the type error, if there is one
* @param throw_on_error - throw a std::runtime_error on failure if set.
* @return if the type check passes
*/
bool TypeSystem::typecheck(const TypeSpec& expected,
const TypeSpec& actual,
const std::string& error_source_name,
bool print_on_error,
bool throw_on_error) const {
bool success = true;
// first, typecheck the base types:
if (!typecheck_base_types(expected.base_type(), actual.base_type())) {
success = false;
}
// next argument checks:
if (expected.m_arguments.size() == actual.m_arguments.size()) {
for (size_t i = 0; i < expected.m_arguments.size(); i++) {
// don't print/throw because the error would be confusing. Better to fail only the
// outer most check and print a single error message.
if (!typecheck(expected.m_arguments[i], actual.m_arguments[i], "", false, false)) {
success = false;
break;
}
}
} else {
// different sizes of arguments.
if (expected.m_arguments.empty()) {
// we expect zero arguments, but got some. The actual type is more specific, so this is fine.
} else {
// different sizes, and we expected arguments. No good!
success = false;
}
}
if (!success) {
if (print_on_error) {
if (error_source_name.empty()) {
fmt::print("[TypeSystem] Got type \"{}\" when expecting \"{}\"\n", actual.print(),
expected.print());
} else {
fmt::print("[TypeSystem] For {}, got type \"{}\" when expecting \"{}\"\n",
error_source_name, actual.print(), expected.print());
}
}
if (throw_on_error) {
throw std::runtime_error("typecheck failed");
}
}
return success;
}
/*!
* Is actual of type expected? For base types.
*/
bool TypeSystem::typecheck_base_types(const std::string& expected,
const std::string& actual) const {
// just to make sure it exists. (note - could there be a case when it just has to be forward
// declared, but not defined?)
lookup_type(expected);
if (expected == actual) {
lookup_type(actual); // make sure it exists
return true;
}
std::string actual_name = actual;
auto actual_type = lookup_type(actual_name);
while (actual_type->has_parent()) {
actual_name = actual_type->get_parent();
actual_type = lookup_type(actual_name);
if (expected == actual_name) {
return true;
}
}
return false;
}
/*!
* Get a path from type to object.
*/
std::vector<std::string> TypeSystem::get_path_up_tree(const std::string& type) {
auto parent = lookup_type(type)->get_parent();
std::vector<std::string> path = {type};
path.push_back(parent);
auto parent_type = lookup_type(parent);
while (parent_type->has_parent()) {
parent = parent_type->get_parent();
parent_type = lookup_type(parent);
path.push_back(parent);
}
return path;
}
/*!
* Lowest common ancestor of two base types.
*/
std::string TypeSystem::lca_base(const std::string& a, const std::string& b) {
if (a == b) {
return a;
}
auto a_up = get_path_up_tree(a);
auto b_up = get_path_up_tree(b);
int ai = a_up.size() - 1;
int bi = b_up.size() - 1;
std::string* result = nullptr;
while (ai >= 0 && bi >= 0) {
if (a_up.at(ai) == b_up.at(bi)) {
result = &a_up.at(ai);
} else {
break;
}
ai--;
bi--;
}
assert(result);
return *result;
}
/*!
* Lowest common ancestor of two typespecs. Will recursively apply to arguments, if compatible.
* Otherwise arguments are stripped off.
* In a situation like lca("(a b)", "(c d)"), the result will be
* (lca(a, b) lca(b, d)).
*/
TypeSpec TypeSystem::lowest_common_ancestor(const TypeSpec& a, const TypeSpec& b) {
auto result = make_typespec(lca_base(a.base_type(), b.base_type()));
if (!a.m_arguments.empty() && !b.m_arguments.empty() &&
a.m_arguments.size() == b.m_arguments.size()) {
// recursively add arguments
for (size_t i = 0; i < a.m_arguments.size(); i++) {
result.add_arg(lowest_common_ancestor(a.m_arguments.at(i), b.m_arguments.at(i)));
}
}
return result;
}
/*!
* Lowest common ancestor of multiple (or at least one) type.
*/
TypeSpec TypeSystem::lowest_common_ancestor(const std::vector<TypeSpec>& types) {
assert(!types.empty());
if (types.size() == 1) {
return types.front();
}
auto result = lowest_common_ancestor(types.at(0), types.at(1));
for (size_t i = 2; i < types.size(); i++) {
result = lowest_common_ancestor(result, types.at(i));
}
return result;
}
+17 -11
View File
@@ -23,6 +23,7 @@ struct DerefInfo {
bool sign_extend = false;
RegKind reg = RegKind::INVALID;
int stride = -1;
int load_size = -1;
TypeSpec result_type;
};
@@ -45,8 +46,8 @@ class TypeSystem {
TypeSpec make_inline_array_typespec(const std::string& type);
TypeSpec make_inline_array_typespec(const TypeSpec& type);
Type* lookup_type(const TypeSpec& ts);
Type* lookup_type(const std::string& name);
Type* lookup_type(const TypeSpec& ts) const;
Type* lookup_type(const std::string& name) const;
MethodInfo add_method(Type* type, const std::string& method_name, const TypeSpec& ts);
MethodInfo add_new_method(Type* type, const TypeSpec& ts);
@@ -67,6 +68,12 @@ class TypeSystem {
void add_builtin_types();
std::string print_all_type_information() const;
bool typecheck(const TypeSpec& expected,
const TypeSpec& actual,
const std::string& error_source_name = "",
bool print_on_error = true,
bool throw_on_error = true) const;
std::vector<std::string> get_path_up_tree(const std::string& type);
/*!
* Get a type by name and cast to a child class of Type*. Must succeed.
@@ -81,19 +88,19 @@ class TypeSystem {
return result;
}
TypeSpec lowest_common_ancestor(const TypeSpec& a, const TypeSpec& b);
TypeSpec lowest_common_ancestor(const std::vector<TypeSpec>& types);
private:
std::string lca_base(const std::string& a, const std::string& b);
bool typecheck_base_types(const std::string& expected, const std::string& actual) const;
int get_size_in_type(const Field& field);
int get_alignment_in_type(const Field& field);
Field lookup_field(const std::string& type_name, const std::string& field_name);
int get_next_method_id(Type* type);
int manual_add_field_to_type(StructureType* type,
const std::string& field_name,
const TypeSpec& field_type,
int offset,
int size,
int alignment);
StructureType* add_builtin_structure(const std::string& parent, const std::string& type_name);
StructureType* add_builtin_structure(const std::string& parent,
const std::string& type_name,
bool boxed = false);
BasicType* add_builtin_basic(const std::string& parent, const std::string& type_name);
ValueType* add_builtin_value_type(const std::string& parent,
const std::string& type_name,
@@ -104,7 +111,6 @@ class TypeSystem {
void builtin_structure_inherit(StructureType* st);
std::unordered_map<std::string, std::unique_ptr<Type>> m_types;
std::unordered_map<std::string, Type*> m_global_types;
std::unordered_set<std::string> m_forward_declared_types;
std::vector<std::unique_ptr<Type>> m_old_types;
+2 -2
View File
@@ -1,9 +1,9 @@
#ifndef JAK_TYPE_UTIL_H
#define JAK_TYPE_UTIL_H
template<typename T>
template <typename T>
T align(T current, T alignment, T offset = 0) {
while((current % alignment) != 0) {
while ((current % alignment) != 0) {
current++;
}
return current + offset;
+1 -1
View File
@@ -12,7 +12,7 @@ namespace versions {
// language version
constexpr s32 GOAL_VERSION_MAJOR = 2;
constexpr s32 GOAL_VERSION_MINOR = 6;
}
} // namespace versions
// GOAL kernel version
constexpr int KERNEL_VERSION_MAJOR = 2;
+8 -12
View File
@@ -9,18 +9,18 @@
struct FunctionName {
enum class FunctionKind {
UNIDENTIFIED, // hasn't been identified yet.
GLOBAL, // global named function
UNIDENTIFIED, // hasn't been identified yet.
GLOBAL, // global named function
METHOD,
TOP_LEVEL_INIT,
} kind = FunctionKind::UNIDENTIFIED;
std::string function_name; // only applicable for GLOBAL
std::string type_name; // only applicable for METHOD
int method_id = -1; // only applicable for METHOD
std::string function_name; // only applicable for GLOBAL
std::string type_name; // only applicable for METHOD
int method_id = -1; // only applicable for METHOD
std::string to_string() const {
switch(kind) {
switch (kind) {
case FunctionKind::GLOBAL:
return function_name;
case FunctionKind::METHOD:
@@ -34,13 +34,9 @@ struct FunctionName {
}
}
bool empty() const {
return kind == FunctionKind::UNIDENTIFIED;
}
bool empty() const { return kind == FunctionKind::UNIDENTIFIED; }
void set_as_top_level() {
kind = FunctionKind::TOP_LEVEL_INIT;
}
void set_as_top_level() { kind = FunctionKind::TOP_LEVEL_INIT; }
void set_as_global(std::string name) {
kind = FunctionKind::GLOBAL;
+4 -6
View File
@@ -90,7 +90,7 @@ Function& LinkedObjectFile::get_function_at_label(int label_id) {
}
assert(false);
return functions_by_seg.front().front(); // to avoid error
return functions_by_seg.front().front(); // to avoid error
}
/*!
@@ -520,7 +520,7 @@ std::string LinkedObjectFile::print_disassembly() {
result += "; .function " + func.guessed_name.to_string() + "\n";
result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n";
result += func.prologue.to_string(2) + "\n";
if(!func.warnings.empty()) {
if (!func.warnings.empty()) {
result += "Warnings: " + func.warnings + "\n";
}
@@ -581,11 +581,11 @@ std::string LinkedObjectFile::print_disassembly() {
// }
// hack
if(func.cfg && !func.cfg->is_fully_resolved()) {
if (func.cfg && !func.cfg->is_fully_resolved()) {
result += func.cfg->to_dot();
result += "\n";
}
if(func.cfg) {
if (func.cfg) {
result += func.cfg->to_form_string() + "\n";
// To debug block stuff.
@@ -614,7 +614,6 @@ std::string LinkedObjectFile::print_disassembly() {
*/
}
result += "\n\n\n";
}
@@ -636,7 +635,6 @@ std::string LinkedObjectFile::print_disassembly() {
if (word.kind == LinkedWord::TYPE_PTR && word.symbol_name == "string") {
result += "; " + get_goal_string(seg, i) + "\n";
}
}
}
+14 -11
View File
@@ -15,7 +15,6 @@
#include "decompiler/Function/Function.h"
#include "decompiler/util/LispPrint.h"
/*!
* A label to a location in this object file.
* Doesn't have to be word aligned.
@@ -23,14 +22,14 @@
struct Label {
std::string name;
int target_segment;
int offset; // in bytes
int offset; // in bytes
};
/*!
* An object file's data with linking information included.
*/
class LinkedObjectFile {
public:
public:
LinkedObjectFile() = default;
void set_segment_count(int n_segs);
void push_back_word_to_segment(uint32_t word, int segment);
@@ -38,8 +37,15 @@ public:
int get_label_at(int seg, int offset) const;
bool label_points_to_code(int label_id) const;
bool pointer_link_word(int source_segment, int source_offset, int dest_segment, int dest_offset);
void pointer_link_split_word(int source_segment, int source_hi_offset, int source_lo_offset, int dest_segment, int dest_offset);
void symbol_link_word(int source_segment, int source_offset, const char* name, LinkedWord::Kind kind);
void pointer_link_split_word(int source_segment,
int source_hi_offset,
int source_lo_offset,
int dest_segment,
int dest_offset);
void symbol_link_word(int source_segment,
int source_offset,
const char* name,
LinkedWord::Kind kind);
void symbol_link_offset(int source_segment, int source_offset, const char* name);
Function& get_function_at_label(int label_id);
std::string get_label_name(int label_id) const;
@@ -83,7 +89,6 @@ public:
uint32_t n_fp_reg_use = 0;
uint32_t n_fp_reg_use_resolved = 0;
void add(const Stats& other) {
total_code_bytes += other.total_code_bytes;
total_v2_code_bytes += other.total_v2_code_bytes;
@@ -116,9 +121,9 @@ public:
std::vector<std::vector<Function>> functions_by_seg;
std::vector<Label> labels;
private:
private:
std::shared_ptr<Form> to_form_script(int seg, int word_idx, std::vector<bool>& seen);
std::shared_ptr<Form> to_form_script_object(int seg, int byte_idx, std::vector<bool> &seen);
std::shared_ptr<Form> to_form_script_object(int seg, int byte_idx, std::vector<bool>& seen);
bool is_empty_list(int seg, int byte_idx);
bool is_string(int seg, int byte_idx);
std::string get_goal_string(int seg, int word_idx);
@@ -126,6 +131,4 @@ private:
std::vector<std::unordered_map<int, int>> label_per_seg_by_offset;
};
#endif //NEXT_LINKEDOBJECTFILE_H
#endif // NEXT_LINKEDOBJECTFILE_H
@@ -213,7 +213,6 @@ static uint32_t align16(uint32_t in) {
return (in + 15) & (~15);
}
/*!
* Process link data for a "V4" object file.
* In reality a V4 seems to be just a V2 object, but with the link data after the real data.
@@ -11,4 +11,4 @@
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data, const std::string& name);
#endif //NEXT_LINKEDOBJECTFILECREATION_H
#endif // NEXT_LINKEDOBJECTFILECREATION_H
+38 -35
View File
@@ -421,8 +421,8 @@ void ObjectFileDB::analyze_functions() {
(void)segment_id;
auto name = func.guessed_name.to_string();
if (func.guessed_name.expected_unique()) {
if(unique_names.find(name) != unique_names.end()) {
duplicated_functions[name].insert(data.record.to_unique_name());
if (unique_names.find(name) != unique_names.end()) {
duplicated_functions[name].insert(data.record.to_unique_name());
}
unique_names.insert(name);
@@ -435,22 +435,22 @@ void ObjectFileDB::analyze_functions() {
});
for_each_function([&](Function& func, int segment_id, ObjectFileData& data) {
(void)segment_id;
auto name = func.guessed_name.to_string();
if(func.guessed_name.expected_unique()) {
if(duplicated_functions.find(name) != duplicated_functions.end()) {
duplicated_functions[name].insert(data.record.to_unique_name());
func.warnings += "this function exists in multiple non-identical object files";
}
(void)segment_id;
auto name = func.guessed_name.to_string();
if (func.guessed_name.expected_unique()) {
if (duplicated_functions.find(name) != duplicated_functions.end()) {
duplicated_functions[name].insert(data.record.to_unique_name());
func.warnings += "this function exists in multiple non-identical object files";
}
}
});
// for(const auto& kv : duplicated_functions) {
// printf("Function %s is found in non-identical object files:\n", kv.first.c_str());
// for(const auto& obj : kv.second) {
// printf(" %s\n", obj.c_str());
// }
// }
// for(const auto& kv : duplicated_functions) {
// printf("Function %s is found in non-identical object files:\n", kv.first.c_str());
// for(const auto& obj : kv.second) {
// printf(" %s\n", obj.c_str());
// }
// }
}
int total_nontrivial_functions = 0;
@@ -466,45 +466,48 @@ void ObjectFileDB::analyze_functions() {
total_basic_blocks += blocks.size();
func.basic_blocks = blocks;
if(!func.suspected_asm) {
func.analyze_prologue(data.linked_data);
func.cfg = build_cfg(data.linked_data, segment_id, func);
total_functions++;
if (func.cfg->is_fully_resolved()) {
resolved_cfg_functions++;
}
if (!func.suspected_asm) {
func.analyze_prologue(data.linked_data);
func.cfg = build_cfg(data.linked_data, segment_id, func);
total_functions++;
if (func.cfg->is_fully_resolved()) {
resolved_cfg_functions++;
}
} else {
resolved_cfg_functions++;
}
if(func.basic_blocks.size() > 1 && !func.suspected_asm) {
if (func.basic_blocks.size() > 1 && !func.suspected_asm) {
total_nontrivial_functions++;
if(func.cfg->is_fully_resolved()) {
if (func.cfg->is_fully_resolved()) {
total_resolved_nontrivial_functions++;
} else {
if(!func.guessed_name.empty()) {
unresolved_by_length[func.end_word - func.start_word].push_back(func.guessed_name.to_string());
if (!func.guessed_name.empty()) {
unresolved_by_length[func.end_word - func.start_word].push_back(
func.guessed_name.to_string());
}
}
}
if(!func.guessed_name.empty()) {
if (!func.guessed_name.empty()) {
total_named_functions++;
}
});
printf("Found %d functions (%d with nontrivial cfgs)\n", total_functions, total_nontrivial_functions);
printf("Named %d/%d functions (%.2f%%)\n", total_named_functions, total_functions, 100.f * float(total_named_functions) / float(total_functions));
printf("Found %d functions (%d with nontrivial cfgs)\n", total_functions,
total_nontrivial_functions);
printf("Named %d/%d functions (%.2f%%)\n", total_named_functions, total_functions,
100.f * float(total_named_functions) / float(total_functions));
printf("Found %d basic blocks in %.3f ms\n", total_basic_blocks, timer.getMs());
printf(" %d/%d functions passed cfg analysis stage (%.2f%%)\n", resolved_cfg_functions, total_functions,
100.f * float(resolved_cfg_functions) / float(total_functions));
printf(" %d/%d nontrivial cfg's resolved (%.2f%%)\n", total_resolved_nontrivial_functions, total_nontrivial_functions,
printf(" %d/%d functions passed cfg analysis stage (%.2f%%)\n", resolved_cfg_functions,
total_functions, 100.f * float(resolved_cfg_functions) / float(total_functions));
printf(" %d/%d nontrivial cfg's resolved (%.2f%%)\n", total_resolved_nontrivial_functions,
total_nontrivial_functions,
100.f * float(total_resolved_nontrivial_functions) / float(total_nontrivial_functions));
for(auto& kv : unresolved_by_length) {
for (auto& kv : unresolved_by_length) {
printf("LEN %d\n", kv.first);
for(auto& x : kv.second) {
for (auto& x : kv.second) {
printf(" %s\n", x.c_str());
}
}
+5 -5
View File
@@ -60,8 +60,8 @@ class ObjectFileDB {
template <typename Func>
void for_each_obj(Func f) {
assert(obj_files_by_name.size() == obj_file_order.size());
for(const auto& name : obj_file_order) {
for(auto& obj : obj_files_by_name.at(name)) {
for (const auto& name : obj_file_order) {
for (auto& obj : obj_files_by_name.at(name)) {
f(obj);
}
}
@@ -75,12 +75,12 @@ class ObjectFileDB {
template <typename Func>
void for_each_function(Func f) {
for_each_obj([&](ObjectFileData& data) {
// printf("IN %s\n", data.record.to_unique_name().c_str());
// printf("IN %s\n", data.record.to_unique_name().c_str());
for (int i = 0; i < int(data.linked_data.segments); i++) {
// printf("seg %d\n", i);
// printf("seg %d\n", i);
int fn = 0;
for (auto& goal_func : data.linked_data.functions_by_seg.at(i)) {
// printf("fn %d\n", fn);
// printf("fn %d\n", fn);
f(goal_func, i, data);
fn++;
}
+7 -7
View File
@@ -3,13 +3,13 @@
class GoalFunction {
public:
// enum Kind {
// GLOBAL_FUNCTION,
// ANON_FUNCTION,
// METHOD,
// BEHAVIOR,
// UNKNOWN
// };
// enum Kind {
// GLOBAL_FUNCTION,
// ANON_FUNCTION,
// METHOD,
// BEHAVIOR,
// UNKNOWN
// };
};
#endif // JAK_DISASSEMBLER_GOALFUNCTION_H
+5 -6
View File
@@ -13,14 +13,13 @@ class GoalSymbol {
m_has_type_info = true;
}
bool has_type_info() const {
return m_has_type_info;
}
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());
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);
}
}
+3 -7
View File
@@ -6,14 +6,10 @@
class GoalType {
public:
GoalType() = default;
GoalType(std::string name) : m_name(std::move(name)) { }
bool has_info() const {
return m_has_info;
}
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;
}
bool has_method_count() const { return m_method_count_set; }
void set_methods(int n);
+7 -6
View File
@@ -48,9 +48,10 @@ std::string TypeInfo::get_summary() {
" 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));
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};
}
@@ -67,7 +68,7 @@ void TypeInfo::inform_symbol_with_no_type_info(const std::string& name) {
}
}
void TypeInfo::inform_symbol(const std::string &name, TypeSpec type) {
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));
}
@@ -88,10 +89,10 @@ void TypeInfo::inform_type_method_count(const std::string& name, int 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) {
for (auto& x : m_symbols) {
result += "\"" + x.first + "\",";
}
if(!result.empty()) {
if (!result.empty()) {
result.pop_back();
}
return result + "};";
+3 -2
View File
@@ -8,8 +8,9 @@
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)) { }
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;
+2 -2
View File
@@ -47,7 +47,7 @@ int main(int argc, char** argv) {
}
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());
// printf("%d\n", InstructionKind::EE_OP_MAX);
// printf("%s\n", get_type_info().get_all_symbols_debug().c_str());
return 0;
}
+10 -18
View File
@@ -6,14 +6,13 @@
#include <vector>
class BinaryReader {
public:
BinaryReader(uint8_t* _buffer, uint32_t _size) : buffer(_buffer), size(_size) {
public:
BinaryReader(uint8_t* _buffer, uint32_t _size) : buffer(_buffer), size(_size) {}
}
explicit BinaryReader(std::vector<uint8_t>& _buffer)
: buffer((uint8_t*)_buffer.data()), size(_buffer.size()) {}
explicit BinaryReader(std::vector<uint8_t>& _buffer) : buffer((uint8_t*)_buffer.data()), size(_buffer.size()) { }
template<typename T>
template <typename T>
T read() {
assert(seek + sizeof(T) <= size);
T& obj = *(T*)(buffer + seek);
@@ -26,23 +25,16 @@ public:
assert(seek <= size);
}
uint32_t bytes_left() const {
return size - seek;
}
uint32_t bytes_left() const { return size - seek; }
uint8_t* here() {
return buffer + seek;
}
uint8_t* here() { return buffer + seek; }
uint32_t get_seek() {
return seek;
}
uint32_t get_seek() { return seek; }
private:
private:
uint8_t* buffer;
uint32_t size;
uint32_t seek = 0;
};
#endif //JAK_V2_BINARYREADER_H
#endif // JAK_V2_BINARYREADER_H
+6 -6
View File
@@ -16,7 +16,8 @@ std::string combine_path(const std::string& parent, const std::string& child) {
std::vector<uint8_t> read_binary_file(const std::string& filename) {
auto fp = fopen(filename.c_str(), "rb");
if(!fp) throw std::runtime_error("File " + filename + " cannot be opened");
if (!fp)
throw std::runtime_error("File " + filename + " cannot be opened");
fseek(fp, 0, SEEK_END);
auto len = ftell(fp);
rewind(fp);
@@ -24,7 +25,7 @@ std::vector<uint8_t> read_binary_file(const std::string& filename) {
std::vector<uint8_t> data;
data.resize(len);
if(fread(data.data(), len, 1, fp) != 1) {
if (fread(data.data(), len, 1, fp) != 1) {
throw std::runtime_error("File " + filename + " cannot be read");
}
@@ -34,8 +35,8 @@ std::vector<uint8_t> read_binary_file(const std::string& filename) {
std::string base_name(const std::string& filename) {
size_t pos = 0;
assert(!filename.empty());
for(size_t i = filename.size() - 1; i-- > 0;) {
if(filename.at(i) == '/') {
for (size_t i = filename.size() - 1; i-- > 0;) {
if (filename.at(i) == '/') {
pos = (i + 1);
break;
}
@@ -66,14 +67,13 @@ uint32_t crc32(const uint8_t* data, size_t size) {
return ~crc;
}
uint32_t crc32(const std::vector<uint8_t>& data) {
return crc32(data.data(), data.size());
}
void write_text_file(const std::string& file_name, const std::string& text) {
FILE* fp = fopen(file_name.c_str(), "w");
if(!fp) {
if (!fp) {
printf("Failed to fopen %s\n", file_name.c_str());
throw std::runtime_error("Failed to open file");
}
+1 -1
View File
@@ -14,4 +14,4 @@ void init_crc();
uint32_t crc32(const uint8_t* data, size_t size);
uint32_t crc32(const std::vector<uint8_t>& data);
#endif //JAK_V2_FILEIO_H
#endif // JAK_V2_FILEIO_H
+98 -96
View File
@@ -45,11 +45,11 @@ std::string Form::toStringSimple() {
return result;
}
void Form::buildStringSimple(std::string &str) {
void Form::buildStringSimple(std::string& str) {
std::vector<FormToken> tokens;
toTokenList(tokens);
for(auto& token : tokens) {
switch(token.kind) {
for (auto& token : tokens) {
switch (token.kind) {
case TokenKind::WHITESPACE:
str.push_back(' ');
break;
@@ -77,26 +77,25 @@ void Form::buildStringSimple(std::string &str) {
}
}
void Form::toTokenList(std::vector<FormToken> &tokens) {
switch(kind) {
void Form::toTokenList(std::vector<FormToken>& tokens) {
switch (kind) {
case FormKind::SYMBOL:
tokens.emplace_back(TokenKind::SYMBOL, symbol);
break;
case FormKind::PAIR:
{
case FormKind::PAIR: {
tokens.emplace_back(TokenKind::OPEN_PAREN);
Form* toPrint = this;
for(;;) {
if(toPrint->kind == FormKind::PAIR) {
toPrint->pair[0]->toTokenList(tokens); // print CAR
for (;;) {
if (toPrint->kind == FormKind::PAIR) {
toPrint->pair[0]->toTokenList(tokens); // print CAR
toPrint = toPrint->pair[1].get();
if(toPrint->kind == FormKind::EMPTY_LIST) {
if (toPrint->kind == FormKind::EMPTY_LIST) {
tokens.emplace_back(TokenKind::CLOSE_PAREN);
return;
} else {
tokens.emplace_back(TokenKind::WHITESPACE);
}
} else { // not a proper list!
} else { // not a proper list!
tokens.emplace_back(TokenKind::DOT);
tokens.emplace_back(TokenKind::WHITESPACE);
toPrint->toTokenList(tokens);
@@ -104,8 +103,7 @@ void Form::toTokenList(std::vector<FormToken> &tokens) {
return;
}
}
}
break;
} break;
case FormKind::EMPTY_LIST:
tokens.emplace_back(TokenKind::EMPTY_PAIR);
break;
@@ -123,25 +121,25 @@ void Form::toTokenList(std::vector<FormToken> &tokens) {
* Linked list node representing a token in the output (whitespace, paren, newline, etc)
*/
struct PrettyPrinterNode {
FormToken* tok = nullptr; // if we aren't a newline, we will have a token.
int line = -1; // line that token occurs on. undef for newlines
int lineIndent = -1; // indent of line. only valid for first token in the line
int offset = -1; // offset of beginning of token from left margin
FormToken* tok = nullptr; // if we aren't a newline, we will have a token.
int line = -1; // line that token occurs on. undef for newlines
int lineIndent = -1; // indent of line. only valid for first token in the line
int offset = -1; // offset of beginning of token from left margin
int specialIndentDelta = 0;
bool is_line_separator = false; // true if line separator (not a token)
PrettyPrinterNode *next = nullptr, *prev = nullptr; // linked list
PrettyPrinterNode *paren = nullptr; // pointer to open paren if in parens. open paren points to close and vice versa
explicit PrettyPrinterNode(FormToken& _tok) {
tok = &_tok;
}
bool is_line_separator = false; // true if line separator (not a token)
PrettyPrinterNode *next = nullptr, *prev = nullptr; // linked list
PrettyPrinterNode* paren =
nullptr; // pointer to open paren if in parens. open paren points to close and vice versa
explicit PrettyPrinterNode(FormToken& _tok) { tok = &_tok; }
PrettyPrinterNode() = default;
};
/*!
* Splice in a line break after the given node, it there isn't one already and if it isn't the last node.
* Splice in a line break after the given node, it there isn't one already and if it isn't the last
* node.
*/
static void insertNewlineAfter(PrettyPrinterNode* node, int specialIndentDelta) {
if(node->next && !node->next->is_line_separator) {
if (node->next && !node->next->is_line_separator) {
auto* nl = new PrettyPrinterNode;
auto* next = node->next;
node->next = nl;
@@ -154,10 +152,11 @@ static void insertNewlineAfter(PrettyPrinterNode* node, int specialIndentDelta)
}
/*!
* Splice in a line break before the given node, if there isn't one already and if it isn't the first node.
* Splice in a line break before the given node, if there isn't one already and if it isn't the
* first node.
*/
static void insertNewlineBefore(PrettyPrinterNode* node, int specialIndentDelta) {
if(node->prev && !node->prev->is_line_separator) {
if (node->prev && !node->prev->is_line_separator) {
auto* nl = new PrettyPrinterNode;
auto* prev = node->prev;
prev->next = nl;
@@ -178,13 +177,13 @@ static void breakList(PrettyPrinterNode* leftParen) {
auto* rp = leftParen->paren;
assert(rp->tok->kind == TokenKind::CLOSE_PAREN);
for(auto* n = leftParen->next; n && n != rp; n = n->next) {
if(!n->is_line_separator) {
if(n->tok->kind == TokenKind::OPEN_PAREN) {
for (auto* n = leftParen->next; n && n != rp; n = n->next) {
if (!n->is_line_separator) {
if (n->tok->kind == TokenKind::OPEN_PAREN) {
n = n->paren;
assert(n->tok->kind == TokenKind::CLOSE_PAREN);
insertNewlineAfter(n, 0);
} else if(n->tok->kind != TokenKind::WHITESPACE) {
} else if (n->tok->kind != TokenKind::WHITESPACE) {
assert(n->tok->kind != TokenKind::CLOSE_PAREN);
insertNewlineAfter(n, 0);
}
@@ -200,19 +199,19 @@ static PrettyPrinterNode* propagatePretty(PrettyPrinterNode* list, int line_leng
// propagate line numbers
PrettyPrinterNode* rv = nullptr;
int line = list->line;
for(auto* n = list; n; n = n->next) {
if(n->is_line_separator) {
for (auto* n = list; n; n = n->next) {
if (n->is_line_separator) {
line++;
} else {
n->line = line;
// add the weird newline.
if(n->tok->kind == TokenKind::CLOSE_PAREN) {
if(n->line != n->paren->line) {
if(n->prev && !n->prev->is_line_separator) {
if (n->tok->kind == TokenKind::CLOSE_PAREN) {
if (n->line != n->paren->line) {
if (n->prev && !n->prev->is_line_separator) {
insertNewlineBefore(n, 0);
line++;
}
if(n->next && !n->next->is_line_separator) {
if (n->next && !n->next->is_line_separator) {
insertNewlineAfter(n, 0);
}
}
@@ -226,12 +225,12 @@ static PrettyPrinterNode* propagatePretty(PrettyPrinterNode* list, int line_leng
int offset = 0;
PrettyPrinterNode* line_start = list;
bool previous_line_sep = false;
for(auto* n = list; n; n = n->next) {
if(n->is_line_separator) {
for (auto* n = list; n; n = n->next) {
if (n->is_line_separator) {
previous_line_sep = true;
offset = indentStack.back() += n->specialIndentDelta;
} else {
if(previous_line_sep) {
if (previous_line_sep) {
line_start = n;
n->lineIndent = offset;
previous_line_sep = false;
@@ -239,21 +238,20 @@ static PrettyPrinterNode* propagatePretty(PrettyPrinterNode* list, int line_leng
n->offset = offset;
offset += n->tok->toString().length();
if(offset > line_length && !rv) rv = line_start;
if(n->tok->kind == TokenKind::OPEN_PAREN) {
if(!n->prev || n->prev->is_line_separator) {
if (offset > line_length && !rv)
rv = line_start;
if (n->tok->kind == TokenKind::OPEN_PAREN) {
if (!n->prev || n->prev->is_line_separator) {
indentStack.push_back(offset + 1);
} else {
indentStack.push_back(offset - 1);
}
}
if(n->tok->kind == TokenKind::CLOSE_PAREN) {
if (n->tok->kind == TokenKind::CLOSE_PAREN) {
indentStack.pop_back();
}
}
}
return rv;
}
@@ -264,9 +262,9 @@ static PrettyPrinterNode* propagatePretty(PrettyPrinterNode* list, int line_leng
static PrettyPrinterNode* getNextLine(PrettyPrinterNode* start) {
assert(!start->is_line_separator);
int line = start->line;
for(;;) {
if(start->is_line_separator || start->line == line) {
if(start->next)
for (;;) {
if (start->is_line_separator || start->line == line) {
if (start->next)
start = start->next;
else
return nullptr;
@@ -278,32 +276,37 @@ static PrettyPrinterNode* getNextLine(PrettyPrinterNode* start) {
}
/*!
* Get the next open paren on the current line (can start in the middle of line, not inclusive of start)
* nullptr if there's no open parens on the rest of this line.
* Get the next open paren on the current line (can start in the middle of line, not inclusive of
* start) nullptr if there's no open parens on the rest of this line.
*/
static PrettyPrinterNode* getNextListOnLine(PrettyPrinterNode* start) {
int line = start->line;
assert(!start->is_line_separator);
if(!start->next || start->next->is_line_separator) return nullptr;
if (!start->next || start->next->is_line_separator)
return nullptr;
start = start->next;
while(!start->is_line_separator && start->line == line) {
if(start->tok->kind == TokenKind::OPEN_PAREN) return start;
if(!start->next) return nullptr;
while (!start->is_line_separator && start->line == line) {
if (start->tok->kind == TokenKind::OPEN_PAREN)
return start;
if (!start->next)
return nullptr;
start = start->next;
}
return nullptr;
}
/*!
* Get the first open paren on the current line (can start in the middle of line, inclusive of start)
* nullptr if there's no open parens on the rest of this line
* Get the first open paren on the current line (can start in the middle of line, inclusive of
* start) nullptr if there's no open parens on the rest of this line
*/
static PrettyPrinterNode* getFirstListOnLine(PrettyPrinterNode* start) {
int line = start->line;
assert(!start->is_line_separator);
while(!start->is_line_separator && start->line == line) {
if(start->tok->kind == TokenKind::OPEN_PAREN) return start;
if(!start->next) return nullptr;
while (!start->is_line_separator && start->line == line) {
if (start->tok->kind == TokenKind::OPEN_PAREN)
return start;
if (!start->next)
return nullptr;
start = start->next;
}
return nullptr;
@@ -316,19 +319,19 @@ static PrettyPrinterNode* getFirstBadLine(PrettyPrinterNode* start, int line_len
assert(!start->is_line_separator);
int currentLine = start->line;
auto* currentLineNode = start;
for(;;) {
if(start->is_line_separator) {
for (;;) {
if (start->is_line_separator) {
assert(start->next);
start = start->next;
} else {
if(start->line != currentLine) {
if (start->line != currentLine) {
currentLine = start->line;
currentLineNode = start;
}
if(start->offset > line_length) {
if (start->offset > line_length) {
return currentLineNode;
}
if(!start->next) {
if (!start->next) {
return nullptr;
}
start = start->next;
@@ -344,55 +347,54 @@ static void insertBreaksAsNeeded(PrettyPrinterNode* head, int line_length) {
PrettyPrinterNode* line_to_start_line_search = head;
// loop over lines
for(;;) {
for (;;) {
// compute lines as needed
propagatePretty(head, line_length);
// search for a bad line starting at the last line we fixed
PrettyPrinterNode* candidate_line = getFirstBadLine(line_to_start_line_search, line_length);
// if we got the same line we started on, this means we couldn't fix it.
if(candidate_line == last_line_complete) {
candidate_line = nullptr; // so we say our candidate was bad and try to find another
if (candidate_line == last_line_complete) {
candidate_line = nullptr; // so we say our candidate was bad and try to find another
PrettyPrinterNode* next_line = getNextLine(line_to_start_line_search);
if(next_line) {
if (next_line) {
candidate_line = getFirstBadLine(next_line, line_length);
}
}
if(!candidate_line) break;
if (!candidate_line)
break;
// okay, we have a line which needs fixing.
assert(!candidate_line->prev || candidate_line->prev->is_line_separator);
PrettyPrinterNode* form_to_start = getFirstListOnLine(candidate_line);
for(;;) {
if(!form_to_start) {
for (;;) {
if (!form_to_start) {
printf("pretty printer has failed. Fix the bug or increase the the line length.\n");
assert(false);
}
breakList(form_to_start);
propagatePretty(head, line_length);
if(getFirstBadLine(candidate_line, line_length) != candidate_line) {
if (getFirstBadLine(candidate_line, line_length) != candidate_line) {
break;
}
form_to_start = getNextListOnLine(form_to_start);
if(!form_to_start) break;
if (!form_to_start)
break;
}
last_line_complete = candidate_line;
line_to_start_line_search = candidate_line;
}
}
static void insertSpecialBreaks(PrettyPrinterNode* node) {
for(; node; node = node->next) {
if(!node->is_line_separator && node->tok->kind == TokenKind::SYMBOL) {
for (; node; node = node->next) {
if (!node->is_line_separator && node->tok->kind == TokenKind::SYMBOL) {
std::string& name = *node->tok->str;
if(name == "deftype") {
if (name == "deftype") {
auto* parent_type_dec = getNextListOnLine(node);
if(parent_type_dec) {
if (parent_type_dec) {
insertNewlineAfter(parent_type_dec->paren, 0);
}
}
@@ -415,7 +417,7 @@ std::string Form::toStringPretty(int indent, int line_length) {
head->offset = 0;
head->lineIndent = 0;
int offset = head->tok->toString().length();
for(size_t i = 1; i < tokens.size(); i++) {
for (size_t i = 1; i < tokens.size(); i++) {
node->next = new PrettyPrinterNode(tokens[i]);
node->next->prev = node;
node = node->next;
@@ -428,10 +430,10 @@ std::string Form::toStringPretty(int indent, int line_length) {
// attach parens.
std::vector<PrettyPrinterNode*> parenStack;
parenStack.push_back(nullptr);
for(PrettyPrinterNode* n = head; n; n = n->next) {
if(n->tok->kind == TokenKind::OPEN_PAREN) {
for (PrettyPrinterNode* n = head; n; n = n->next) {
if (n->tok->kind == TokenKind::OPEN_PAREN) {
parenStack.push_back(n);
} else if(n->tok->kind == TokenKind::CLOSE_PAREN) {
} else if (n->tok->kind == TokenKind::CLOSE_PAREN) {
n->paren = parenStack.back();
parenStack.back()->paren = n;
parenStack.pop_back();
@@ -446,31 +448,31 @@ std::string Form::toStringPretty(int indent, int line_length) {
propagatePretty(head, line_length);
insertBreaksAsNeeded(head, line_length);
// write to string
bool newline_prev = true;
for(PrettyPrinterNode* n = head; n; n = n->next) {
if(n->is_line_separator){
for (PrettyPrinterNode* n = head; n; n = n->next) {
if (n->is_line_separator) {
pretty.push_back('\n');
newline_prev = true;
} else {
if(newline_prev) {
if (newline_prev) {
pretty.append(n->lineIndent, ' ');
newline_prev = false;
if(n->tok->kind == TokenKind::WHITESPACE) continue;
if (n->tok->kind == TokenKind::WHITESPACE)
continue;
}
pretty.append(n->tok->toString());
}
}
for(;;) {
if(!head) break;
for (;;) {
if (!head)
break;
auto* next = head->next;
delete head;
head = next;
}
return pretty;
}
@@ -497,7 +499,7 @@ std::shared_ptr<Form> buildList(std::shared_ptr<Form>* forms, int count) {
auto f = std::make_shared<Form>();
f->kind = FormKind::PAIR;
f->pair[0] = forms[0];
if(count - 1) {
if (count - 1) {
f->pair[1] = buildList(forms + 1, count - 1);
} else {
f->pair[1] = gSymbolTable.getEmptyPair();
@@ -507,7 +509,7 @@ std::shared_ptr<Form> buildList(std::shared_ptr<Form>* forms, int count) {
}
std::shared_ptr<Form> buildList(std::vector<std::shared_ptr<Form>>& forms) {
if(forms.empty()) {
if (forms.empty()) {
return gSymbolTable.getEmptyPair();
}
return buildList(forms.data(), forms.size());
+24
View File
@@ -0,0 +1,24 @@
# Emitter
x86-64 has a lot of instructions. They are described in Volume 2 of the 5 Volume "Intel® 64 and IA-32 Architectures Software Developers Manual". Just this volume alone is over 2000 pages, which would take forever to fully implement. As a result, we will use only a subset of these instructions. This the rough plan:
- Most instructions like `add` will only be implemented with `r64 r64` versions.
- To accomplish something like `add rax, 1`, we will use a temporary register `X`
- `mov X, 1`
- `add rax, X`
- The constant propagation system will be able to provide enough information that we could eventually use `add r64 immX` and similar if needed.
- Register allocation should handle the case `(set! x (+ 3 y))` as:
- `mov x, 3`
- `add x, y`
- but `(set! x (+ y 3))`, in cases where `y` is needed after and `x` can't take its place, will become the inefficient
- `mov x, y`
- `mov rtemp, 3`
- `add x, rtemp`
- Loading constants into registers will be done efficiently, using the same strategy used by modern versions of `gcc` and `clang`.
- Memory access will be done in the form `mov rdest, [roff + raddr]` where `roff` is the offset register. Doing memory access in this form was found to be much faster in simple benchmark test.
- Memory access to the stack will have an extra `sub` and more complicated dereference. GOAL code seems to avoid using the stack in most places, and I suspect the programmers attempted to avoid stack spills.
- `mov rdest, rsp` : coloring move for upcoming subtract
- `sub rdest, roff` : convert real pointer to GOAL pointer
- `mov rdest, [rdest + roff + variable_offset]` : access memory through normal GOAL deref.
- Note - we should check that the register allocator gets this right always, and eliminates moves and avoid using a temporary register.
- Again, the constant propagation should give use enough information, if we ever want/need to implement a more efficient `mov rdest, [rsp + varaible_offset]` type instructions.
- Memory access to static data should use `rip` addressing, like `mov rdest, [rip + offset]`. And creating pointers to static data could be `lea rdest, [rip - roff + offset]`
+111
View File
@@ -0,0 +1,111 @@
## Registers
Although modern computers are much faster than the PS2, and we could probably get away with a really inefficient register allocation scheme, I think it's worth it to get this right.
## Register differences between MIPS and x86-64
The PS2's MIPS processor has these categories of register:
- General Purpose. They are 128-bit, but usually only lower 64 bits are used. 32 registers, each 128-bits.
- Floating point registers. 32 registers, each for a 32-bit float.
- Vector float registers. 32 registers, each for 4x 32-bit floats. Used only in inline assembly
- `vi` registers. 16 registers, each a 16-bit integer. Used very rarely in inline assembly
There are also some control/special registers too (`Q`, `R`...), but code using these will be manually ported.
In comparison, x86-64 has much fewer registers:
- 16 General Purpose. Each 64-bits
- 16 `xmm` registers. 128-bits, and can store either 128-bit integers or 4x 32-bit floats
Here is the mapping:
- MIPS GPR (lower 64 bits only) - x86-64 GPR
- MIPS GPR (128-bits, only special cases) - x64-64 `xmm`
- MIPS floating point - x64-64 `xmm` (lower 32-bits)
- MIPS vector float - x64-64 `xmm` (packed single)
- MIPS `vi` - manually handled??
Here is the MIPS GPR map
- `r0` or `zero` : always zero
- `r1` or `at`: assembler temporary, not saved, not used by compiler
- `r2` or `v0`: return value, not saved
- `r3` or `v1`: not saved
- `r4` or `a0`: not saved, argument 0
- `r5` or `a1`: not saved, argument 1
- `r6` or `a2`: not saved, argument 2
- `r7` or `a3`: not saved, argument 3
- `r8` or `t0`: not saved, argument 4
- `r9` or `t1`: not saved, argument 5
- `r10` or `t2`: not saved, argument 6
- `r11` or `t3`: not saved, argument 7
- `r12` or `t4`: not saved
- `r13` or `t5`: not saved
- `r14` or `t6`: not saved
- `r15` or `t7`: not saved
- `r16` or `s0`: saved
- `r17` or `s1`: saved
- `r18` or `s2`: saved
- `r19` or `s3`: saved
- `r20` or `s4`: saved
- `r21` or `s5`: saved
- `r22` or `s6`: saved, process pointer
- `r23` or `s7`: saved, symbol pointer
- `r24` or `t8`: not saved
- `r25` or `t9`: function call pointer
- `r26` or `k0`: kernel reserved (unused)
- `r27` or `k1`: kernel reserved (unused)
- `r28` or `gp`: saved
- `r29` or `sp`: stack pointer
- `r30` or `fp`: current function pointer
- `r31` or `ra`: return address pointer
And the x86-64 GPR map
- `rax`: return value
- `rcx`: argument 3
- `rdx`: argument 2
- `rbx`: saved
- `rsp`: stack pointer
- `rbp`: saved
- `rsi`: argument 1
- `rdi`: argument 0
- `r8`: argument 4
- `r9`: argument 5
- `r10`: argument 6, saved if not argument
- `r11`: argument 7, saved if not argument
- `r12`: saved
- `r13`: process pointer
- `r14`: symbol table
- `r15`: offset pointer
### Plan for Memory Access
The PS2 uses 32-bit pointers, and changing the pointer size is likely to introduce bugs, so we will keep using 32-bit pointers. Also, GOAL has some hardcoded checks on the value for pointers, so we need to make sure the memory appears to the program at the correct address.
To do this, we have separate "GOAL Pointers" and "real pointers". The "real pointers" are just normal x86-64 pointers, and the "GOAL Pointer" is an offset into a main memory array. A "real pointer" to the main memory array is stored in `r15` (offset pointer) when GOAL code is executing, and the GOAL compiler will automatically add this to all memory accesses.
The overhead from doing this is not as bad as you might expect - x86 has nice addressing modes (Scale Index Base) which are quite fast, and don't require the use of temporary registers. If this does turn out to be much slower than I expect, we can introduce the concept of real pointers in GOAL code, and use them in places where we are limited in accessing memory.
The main RAM is mapped at `0x0` on the PS2, with the first 1 MB reserved for the kernel. We should make sure that the first 1 MB of GOAL main memory will cause a segfault if read/written/executed, to catch null pointer bugs.
In the C Kernel code, the `r15` pointer doesn't exist. Instead, `g_ee_main_memory` is a global which points to the beginning of GOAL main memory. The `Ptr<T>` template class takes care of converting GOAL and C++ pointers in a convenient way, and catches null pointer access.
The GOAL stack pointer should likely be a real pointer, for performance reasons. This makes pushing/popping/calling/returning/accessing stack variables much faster, with the only cost being getting a GOAL stack pointer requiring some extra work. The stack pointer's value is read/written extremely rarely, so this seems like a good tradeoff.
The other registers are less clear. The process pointer can probably be a real pointer. But the symbol table could go a few ways:
1. Make it a real pointer. Symbol value access is fast, but comparison against false requires two extra operations.
2. Make it a GOAL pointer. Symbol value access requires more complicated addressing modes, but comparison against false is fast.
Right now I'm leaning toward 1, but making it a configurable option in case I'm wrong. It should only be a change in a few places (emitter + where it's set up in the runtime).
### Plan for Function Call and Arguments
In GOAL for MIPS, function calls are weird. Functions are always called by register using `t9`. There seems to be a different register allocator for function pointers, as nested function calls have really wacky register allocation. In GOAL-x86-64, this restriction will be removed, and a function can be called from any register. (see next section for why we can do this)
Unfortunately, GOAL's 128-bit function arguments present a big challenge. When calling a function, we can't know if the function we're calling is expecting an integer, float, or 128-bit integer. In fact, the caller may not even know if it has an integer, float, or 128-bit integer. The easy and foolproof way to get this right is to use 128-bit `xmm` registers for all arguments and return values, but this will cause a massive performance hit and increase code size, as we'll have to move values between register types constantly. The current plan is this:
- Floats go in GPRs for arguments/return values. GOAL does this too, and takes the hit of converting between registers as well. Probably the impact on a modern CPU is even worse, but we can live with it.
- We'll compromise
### Plan for Static Data
### Plan for Memory
### Other details
+14 -34
View File
@@ -6,7 +6,7 @@ This document explains the GOAL type system. The GOAL type system supports runt
Some objects have runtime type information, and others don't. Objects which have runtime type information can have their type identified at runtime, and are called "boxed objects". Objects without runtime type information are called "unboxed objects". An unboxed object cannot reliably be detected as a unboxed object - you can't write a function that takes an arbitrary object and tells you if its boxed or not. However, boxed objects can always be recognized as boxed.
All types have a parent type, and all types descend from the parent type `object`, except for the special type `none`. The `none` type doesn't exist in the runtime and is used to represent an invalid value that the compiler should not use. For example, the return type of a function which doesn't return anything is `none`, and attempting to use this value should cause an error.
All types have a parent type, and all types descend from the parent type `object`, except for the special type `none` (and maybe `_type_`, but more on this later). The `none` type doesn't exist in the runtime and is used to represent an invalid value that the compiler should not use. For example, the return type of a function which doesn't return anything is `none`, and attempting to use this value should cause an error.
Here are some important special types:
- `object` - the parent of all types
@@ -15,7 +15,7 @@ This document explains the GOAL type system. The GOAL type system supports runt
All types have methods. Objects have access to all of their parents methods, and may override parent methods. All types have these 9 methods:
- `new` - like a constructor, returns a new object. It's not used in all cases, and on all types, and needs more documentation.
- `new` - like a constructor, returns a new object. It's not used in all cases, and on all types, and needs more documentation on when specifically it is used.
- `delete` - basically unused, but like a destructor. Often calls `kfree`, which does nothing.
- `print` - prints a short, one line representation of the object to the `PrintBuffer`
- `inspect` - prints a multi-line description of the object to the `PrintBuffer`. Usually auto-generated by the compiler and prints out the name and value of each field.
@@ -25,7 +25,7 @@ This document explains the GOAL type system. The GOAL type system supports runt
- `relocate` - Some GOAL objects will be moved in memory by the kernel as part of the compacting actor heap system. After being moved, the `relocate` method will be called with the offset of the move, and the object should fix up any internal pointers which may point to the old location. It's also called on v2 objects loaded by the linker when they are first loaded into memory.
- `memusage` - Not understood yet, but probably returns how much memory in bytes the object uses. Not supported by all objects.
Usually a method which overrides a parent method will have the same argument and return types. The only exception is `new` methods, which can have different argument/return types from the parent.
Usually a method which overrides a parent method must have the same argument and return types. The only exception is `new` methods, which can have different argument/return types from the parent. (Dee the later section on `_type_` for another exception)
The compiler's implementation for calling a method is:
- Is the type a basic?
@@ -118,9 +118,8 @@ There's a single type system library, located in `common/type_system`. It will
The main features are:
- `TypeSystem` stores all type information and provides a convenient way to add new types or request information about existing types.
- `Type` a GOAL Type. A `Type` is identified by a single unique string. Examples: `function`, `string`, `vector3h`.
- `Type` information about a GOAL Type. A "base GOAL type" is identified by a single unique string. Examples: `function`, `string`, `vector3h`.
- `TypeSpec` a way to specify either `Type` or a "compound type". Compound types are used to create types which represent specific function types (function which takes two integer arguments and returns a string), or specific pointer/array types (pointer to an integer). These can be represented as (possibly nested) lists, like `(pointer integer)` or `(function (integer integer) string)`.
- `FunctionSpec` (unimplemented) - contains a `TypeSpec` plus some additional information about a function (like if it is a global function, a method, names of the arguments, etc)
- Type Checking for compiler
- Parsing of type definitions for compiler
- Lowest common ancestor implementation for compiler to figure out return types for branching forms.
@@ -187,33 +186,14 @@ Method System
All type definitions should also define all the methods, in the order they appear in the vtable. I suspect GOAL had this as well because the method ordering otherwise seems random, and in some cases impossible to get right unless (at least) the number of methods was specified in the type declaration.
Todo
---------
- [x] Difference between "runtime" and "compile time" types?
- [ ] `inline-array` and `pointer`
- [x] Arrays which aren't `array`s and aren't fields.
- [x] `lookup_field_info` (returning the correct field type for arrays/dynamics, info about how to deref)
- [x] `deref_info`
- [ ] Finish builtin types
- [ ] Tests for...
- [ ] Builtin types
- [ ] Methods
- [ ] Multiple definition checks
- [ ] Deref
- [ ] Array access
- [ ] Field creation
- [ ] Support for `_type_` / method specific stuff. (maybe this should live outside the type system?)
- [ ] Ability to export type in `deftype` form.
- [x] Multiple definition checks
- [ ] Reverse field (offset/deref to fields)
- [ ] Type Checking
- [ ] Function Specs
- [ ] Lowest Common Ancestor
- [x] Document `:inline`, `:dynamic:` and field arrays.
- [x] Document alignment rules
- [ ] Structure type with itself as a field
- [ ] Ability to read a `deftype` form.
- [ ] In the decompiler
- [ ] In the compiler, with the ability to do constant propagation and put things like `(+ 1 2)` or `MY_CONSTANT` as compile-time array size constants by providing a function evaluating an `Object` to an `int`.
- [ ] Bitfield types
-----
- [ ] Kernel types that are built-in
- [ ] Signed/unsigned for a few built-in type fields
- [ ] Tests for field placement logic (probably a full compiler test?)
- [ ] Bitfield types
- [ ] Type redefinition tests (these are a pain and probably useless, might just wait for full compiler tests?)
- [ ] Stuff for decompiler
- [ ] What field is here?
- [ ] Export all deftypes
+15
View File
@@ -199,6 +199,7 @@ char* basename_goal(char* s) {
}
}
/* Original code, has memory bug.
// back up...
for (;;) {
if (pt < input) {
@@ -211,6 +212,20 @@ char* basename_goal(char* s) {
return pt + 1; // and return one past
}
}
*/
// back up...
for (;;) {
if (pt <= input) {
return input;
}
pt--;
char c = *pt;
// until we hit a slash.
if (c == '\\' || c == '/') { // slashes
return pt + 1; // and return one past
}
}
}
/*!
+1
View File
@@ -14,6 +14,7 @@
#include "kboot.h"
#include "kprint.h"
#include "common/symbols.h"
#include "common/goal_constants.h"
namespace {
// turn on printf's for debugging linking issues.
-1
View File
@@ -3,7 +3,6 @@
* Memory card interfaces. Very messy code.
*/
#ifndef JAK_KMEMCARD_H
#define JAK_KMEMCARD_H
+3 -2
View File
@@ -8,6 +8,7 @@
#include <stdarg.h>
#include <stdio.h>
#include "common/goal_constants.h"
#include "common/common_types.h"
#include "kprint.h"
#include "kmachine.h"
@@ -880,7 +881,7 @@ s32 format_impl(uint64_t* args) {
if (sym.offset) {
Ptr<Type> type = *sym.cast<Ptr<Type>>();
if (type.offset) {
call_method_of_type(in, type, GOAL_PRINT_FUNC);
call_method_of_type(in, type, GOAL_PRINT_METHOD);
}
} else {
throw std::runtime_error("failed to find symbol in format!");
@@ -901,7 +902,7 @@ s32 format_impl(uint64_t* args) {
if (sym.offset) {
Ptr<Type> type = *sym.cast<Ptr<Type>>();
if (type.offset) {
call_method_of_type(in, type, GOAL_INSPECT_FUNC);
call_method_of_type(in, type, GOAL_INSPECT_METHOD);
}
} else {
throw std::runtime_error("failed to find symbol in format!");
+5 -3
View File
@@ -18,6 +18,7 @@
#include "klink.h"
#include "common/symbols.h"
#include "common/versions.h"
#include "common/goal_constants.h"
//! Controls link mode when EnableMethodSet = 0, MasterDebug = 1, DiskBoot = 0. Will enable a
//! warning message if EnableMethodSet = 1
@@ -1009,7 +1010,7 @@ u64 print_object(u32 obj) {
} else if ((obj & OFFSET_MASK) == PAIR_OFFSET) {
return print_pair(obj);
} else if ((obj & OFFSET_MASK) == BASIC_OFFSET) {
return call_method_of_type(obj, Ptr<Type>(*Ptr<u32>(obj - 4)), GOAL_PRINT_FUNC);
return call_method_of_type(obj, Ptr<Type>(*Ptr<u32>(obj - 4)), GOAL_PRINT_METHOD);
} else {
cprintf("#<unknown type %d @ #x%x>", obj & OFFSET_MASK, obj);
}
@@ -1217,7 +1218,7 @@ u64 copy_structure(u32 it, u32 unknown) {
u64 copy_basic(u32 obj, u32 heap) {
// determine size of basic. We call a method instead of using asize_of_basic in case the type has
// overridden the default asize_of method.
u32 size = call_method_of_type(obj, Ptr<Type>(*Ptr<u32>(obj - BASIC_OFFSET)), GOAL_ASIZE_FUNC);
u32 size = call_method_of_type(obj, Ptr<Type>(*Ptr<u32>(obj - BASIC_OFFSET)), GOAL_ASIZE_METHOD);
u32 result;
if (*Ptr<u32>(heap - 4) == *(s7 + FIX_SYM_SYMBOL_TYPE)) {
@@ -1247,7 +1248,8 @@ u64 inspect_object(u32 obj) {
} else if ((obj & OFFSET_MASK) == PAIR_OFFSET) {
return inspect_pair(obj);
} else if ((obj & OFFSET_MASK) == BASIC_OFFSET) {
return call_method_of_type(obj, Ptr<Type>(*Ptr<u32>(obj - BASIC_OFFSET)), GOAL_INSPECT_FUNC);
return call_method_of_type(obj, Ptr<Type>(*Ptr<u32>(obj - BASIC_OFFSET)),
GOAL_INSPECT_METHOD);
} else {
cprintf("#<unknown type %d @ #x%x>", obj & OFFSET_MASK, obj);
}
+1 -12
View File
@@ -18,23 +18,12 @@ extern Ptr<u32> SymbolTable2;
extern Ptr<u32> LastSymbol;
constexpr s32 GOAL_MAX_SYMBOLS = 0x2000;
constexpr s32 BINTEGER_OFFSET = 0;
constexpr s32 PAIR_OFFSET = 2;
constexpr s32 BASIC_OFFSET = 4;
constexpr s32 SYM_INFO_OFFSET = 0xff34;
constexpr u32 EMPTY_HASH = 0x8454B6E6;
constexpr u32 OFFSET_MASK = 7;
constexpr u32 CRC_POLY = 0x04c11db7;
constexpr u32 GOAL_NEW_FUNC = 0; // method ID of GOAL new
constexpr u32 GOAL_DEL_FUNC = 1; // method ID of GOAL delete
constexpr u32 GOAL_PRINT_FUNC = 2; // method ID of GOAL print
constexpr u32 GOAL_INSPECT_FUNC = 3; // method ID of GOAL inspect
constexpr u32 GOAL_LENGTH_FUNC = 4; // method ID of GOAL length
constexpr u32 GOAL_ASIZE_FUNC = 5; // method ID of GOAL size
constexpr u32 GOAL_COPY_FUNC = 6; // method ID of GOAL copy
constexpr u32 GOAL_RELOC_FUNC = 7; // method ID of GOAL relocate
constexpr u32 DEFAULT_METHOD_COUNT = 12;
constexpr u32 FALLBACK_UNKNOWN_METHOD_COUNT = 44;
+1 -1
View File
@@ -7,7 +7,7 @@
#include "common/versions.h"
int main(int argc, char** argv) {
while(true) {
while (true) {
// run the runtime in a loop so we can reset the game and have it restart cleanly
printf("gk %d.%d\n", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
exec_runtime(argc, argv);
+1 -1
View File
@@ -76,7 +76,7 @@ void DMA_SendToEE(void* data, u32 size, void* dest) {
if (dmaid == 0) {
do {
printf("Got a bad DMA ID!\n"); // added
printf("Got a bad DMA ID!\n"); // added
} while (true);
}
}
+6 -10
View File
@@ -79,20 +79,16 @@ static const char* next_dir = nullptr;
*/
int FS_Init(u8* buffer) {
(void)buffer;
// get path to next/. Will be set in the gk.sh launch script. This is for Linux only
/*
next_dir = std::getenv("NEXT_DIR"); // todo windows?
// get path to next/. Will be set in the gk.sh launch script.
next_dir = std::getenv("NEXT_DIR");
assert(next_dir);
// get path to next/data/fake_iso.txt, the map file.
char fakeiso_path[512];
strcpy(fakeiso_path, next_dir);
strcat(fakeiso_path, "/game/fake_iso.txt"); // todo windows paths?
*/
char fakeiso_path[] = "C:\\Users\\Shalen\\Documents\\GitHub\\jak-project-windows-proj\\game\\fake_iso.txt";
char NEXT_DIR[] = "C:\\Users\\Shalen\\Documents\\GitHub\\jak-project-windows-proj";
fake_iso_path = std::getenv("FAKE_ISO_PATH");
assert(fake_iso_path);
strcat(fakeiso_path, fake_iso_path);
// open the map.
FILE* fp = fopen(fakeiso_path, "r");
@@ -330,7 +326,7 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
*/
uint32_t FS_SyncRead() {
// FS_BeginRead is blocking, so this is useless.
if(read_in_progress) {
if (read_in_progress) {
read_in_progress = false;
return CMD_STATUS_IN_PROGRESS;
} else {
+1 -1
View File
@@ -17,4 +17,4 @@
void fake_iso_init_globals();
extern IsoFs fake_iso;
#endif //JAK_V2_FAKE_ISO_H
#endif // JAK_V2_FAKE_ISO_H
+1 -2
View File
@@ -767,7 +767,6 @@ u32 ProcessVAGData(IsoMessage* _cmd, IsoBufferHeader* buffer_header) {
// TODO - UpdatePlayPos
// TODO - CheckVAGStreamProgress
void* RPC_DGO(unsigned int fno, void* _cmd, int y);
void LoadDGO(RPC_Dgo_Cmd* cmd);
void LoadNextDGO(RPC_Dgo_Cmd* cmd);
@@ -913,7 +912,7 @@ void CancelDGO(RPC_Dgo_Cmd* cmd) {
SendMbx(sync_mbx, nullptr);
// wait for it to abort.
WaitMbx(dgo_mbx);
assert(cmd); // bug
assert(cmd); // bug
cmd->result = DGO_RPC_RESULT_ABORTED;
scmd.cmd_id = 0;
}
+4 -4
View File
@@ -6,7 +6,7 @@ using namespace iop;
/*!
* Load a File to IOP memory (blocking)
*/
void LoadISOFileToIOP(FileRecord *file, void *addr, uint32_t length) {
void LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length) {
printf("[OVERLORD] LoadISOFileToIOP %s, %d/%d bytes\n", file->name, length, file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_IOP_CMD_ID;
@@ -18,7 +18,7 @@ void LoadISOFileToIOP(FileRecord *file, void *addr, uint32_t length) {
SendMbx(iso_mbx, &cmd);
SleepThread();
if(cmd.status) {
if (cmd.status) {
cmd.length_to_copy = 0;
}
}
@@ -26,7 +26,7 @@ void LoadISOFileToIOP(FileRecord *file, void *addr, uint32_t length) {
/*!
* Load a File to IOP memory (blocking)
*/
void LoadISOFileToEE(FileRecord *file, uint32_t addr, uint32_t length) {
void LoadISOFileToEE(FileRecord* file, uint32_t addr, uint32_t length) {
printf("[OVERLORD] LoadISOFileToEE %s, %d/%d bytes\n", file->name, length, file->size);
IsoCommandLoadSingle cmd;
cmd.cmd_id = LOAD_TO_EE_CMD_ID;
@@ -38,7 +38,7 @@ void LoadISOFileToEE(FileRecord *file, uint32_t addr, uint32_t length) {
SendMbx(iso_mbx, &cmd);
SleepThread();
if(cmd.status) {
if (cmd.status) {
cmd.length_to_copy = 0;
}
}
+3 -3
View File
@@ -2,7 +2,7 @@
#define JAK_V2_ISO_API_H
#include "isocommon.h"
void LoadISOFileToIOP(FileRecord *file, void *addr, uint32_t length);
void LoadISOFileToEE(FileRecord *file, uint32_t ee_addr, uint32_t length);
void LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length);
void LoadISOFileToEE(FileRecord* file, uint32_t ee_addr, uint32_t length);
#endif //JAK_V2_ISO_API_H
#endif // JAK_V2_ISO_API_H
+74 -70
View File
@@ -21,7 +21,6 @@ struct IsoStrBuffer {
u8 data[STR_BUFFER_DATA_SIZE];
};
static IsoBuffer sBuffer[N_BUFFERS];
static IsoStrBuffer sStrBuffer[N_STR_BUFFERS];
static IsoBuffer* sFreeBuffer;
@@ -36,7 +35,7 @@ VagCommand vag_cmds[N_VAG_CMDS];
static s32 sSema;
IsoBufferHeader* TryAllocateBuffer(uint32_t size);
void ReleaseMessage(IsoMessage *cmd);
void ReleaseMessage(IsoMessage* cmd);
void FreeVAGCommand(VagCommand* cmd);
void iso_queue_init_globals() {
@@ -44,7 +43,8 @@ void iso_queue_init_globals() {
memset(sStrBuffer, 0, sizeof(sStrBuffer));
sFreeBuffer = nullptr;
sFreeStrBuffer = nullptr;
for(auto& e : gPriStack) e.reset();
for (auto& e : gPriStack)
e.reset();
vag_cmd_cnt = 0;
vag_cmd_used = 0;
@@ -54,25 +54,25 @@ void iso_queue_init_globals() {
}
void PriStackEntry::reset() {
for(auto& c : cmds) c = nullptr;
for (auto& c : cmds)
c = nullptr;
n = 0;
for(auto& x : names) x.clear();
for (auto& x : names)
x.clear();
}
void InitBuffers() {
// chain all buffers together and set them as free.
for(uint32_t i = 0; i < N_BUFFERS; i++) {
for (uint32_t i = 0; i < N_BUFFERS; i++) {
sBuffer[i].header.data = nullptr;
sBuffer[i].header.data_size = 0;
sBuffer[i].header.buffer_size = BUFFER_PAGE_SIZE;
sBuffer[i].header.next = &sBuffer[i+1].header;
sBuffer[i].header.next = &sBuffer[i + 1].header;
}
sBuffer[N_BUFFERS - 1].header.next = nullptr;
sFreeBuffer = &sBuffer[0];
for(uint32_t i = 0; i < N_STR_BUFFERS; i++) {
for (uint32_t i = 0; i < N_STR_BUFFERS; i++) {
sStrBuffer[i].header.data = nullptr;
sStrBuffer[i].header.data_size = 0;
sStrBuffer[i].header.buffer_size = STR_BUFFER_DATA_SIZE;
@@ -89,19 +89,20 @@ void InitBuffers() {
params.init_count = 0;
sSema = CreateSema(&params);
if(sSema < 0) {
for(;;) {
if (sSema < 0) {
for (;;) {
printf("[OVERLORD] VAG Semaphore creation failed!\n");
}
}
}
/*!
* Allocate a buffer of the given size. If not possible, loop forever. Size must be BUFFER_PAGE_SIZE or STR_BUFFER_DATA_SIZE,
* Allocate a buffer of the given size. If not possible, loop forever. Size must be BUFFER_PAGE_SIZE
* or STR_BUFFER_DATA_SIZE,
*/
IsoBufferHeader* AllocateBuffer(uint32_t size) {
IsoBufferHeader *buffer = TryAllocateBuffer(size);
if(buffer) {
IsoBufferHeader* buffer = TryAllocateBuffer(size);
if (buffer) {
printf("--------------- allocated buffer size %d\n", size);
return buffer;
} else {
@@ -111,17 +112,16 @@ IsoBufferHeader* AllocateBuffer(uint32_t size) {
}
}
/*!
* Allocate a buffer of given size. If the size isn't BUFFER_PAGE_SIZE, you get a streaming buffer (STR_BUFFER_DATA_SIZE).
* If no allocation can be done, return nullptr.
* Allocate a buffer of given size. If the size isn't BUFFER_PAGE_SIZE, you get a streaming buffer
* (STR_BUFFER_DATA_SIZE). If no allocation can be done, return nullptr.
*/
IsoBufferHeader* TryAllocateBuffer(uint32_t size) {
IsoStrBuffer* top_str = sFreeStrBuffer;
IsoBuffer* top_buff = sFreeBuffer;
if(size == BUFFER_PAGE_SIZE) {
if(sFreeBuffer) {
if (size == BUFFER_PAGE_SIZE) {
if (sFreeBuffer) {
auto next = sFreeBuffer->header.next;
sFreeBuffer->header.data = nullptr;
sFreeBuffer = (IsoBuffer*)next;
@@ -130,7 +130,7 @@ IsoBufferHeader* TryAllocateBuffer(uint32_t size) {
return (IsoBufferHeader*)top_buff;
}
} else {
if(sFreeStrBuffer) {
if (sFreeStrBuffer) {
auto next = sFreeStrBuffer->header.next;
sFreeStrBuffer->header.data = nullptr;
sFreeStrBuffer = (IsoStrBuffer*)next;
@@ -146,10 +146,10 @@ IsoBufferHeader* TryAllocateBuffer(uint32_t size) {
/*!
* Return a buffer once you are done using it so somebody else can have a turn
*/
void FreeBuffer(IsoBufferHeader *buffer) {
void FreeBuffer(IsoBufferHeader* buffer) {
IsoBufferHeader* b = (IsoBufferHeader*)buffer;
printf("--------------- free buffer size %d\n", b->buffer_size);
if(b->buffer_size == BUFFER_PAGE_SIZE) {
if (b->buffer_size == BUFFER_PAGE_SIZE) {
b->next = sFreeBuffer;
sFreeBuffer = (IsoBuffer*)b;
} else {
@@ -163,8 +163,8 @@ void FreeBuffer(IsoBufferHeader *buffer) {
* The actual function does nothing.
*/
void DisplayQueue() {
for(int pri = 0; pri < N_PRIORITIES; pri++) {
for(int cmd = 0; cmd < (int)gPriStack[pri].n; cmd++) {
for (int pri = 0; pri < N_PRIORITIES; pri++) {
for (int cmd = 0; cmd < (int)gPriStack[pri].n; cmd++) {
printf(" PRI %d elt %d %s\n", pri, cmd, gPriStack[pri].names[cmd].c_str());
}
}
@@ -175,13 +175,14 @@ void DisplayQueue() {
* If there is no room left in the queue, ReturnMessage with a CMD_STATUS_FAILED_TO_QUEUE.
* Return 1 on success.
*/
u32 QueueMessage(IsoMessage *cmd, int32_t priority, const char *name) {
u32 QueueMessage(IsoMessage* cmd, int32_t priority, const char* name) {
u32 ok = gPriStack[priority].n != PRI_STACK_LENGTH;
if(ok) {
if (ok) {
gPriStack[priority].cmds[gPriStack[priority].n] = cmd;
gPriStack[priority].names[gPriStack[priority].n] = name;
gPriStack[priority].n++;
printf("[OVERLORD] Queue %d (%d/%d), %s\n", priority, gPriStack[priority].n, PRI_STACK_LENGTH, gPriStack[priority].names[gPriStack[priority].n - 1].c_str());
printf("[OVERLORD] Queue %d (%d/%d), %s\n", priority, gPriStack[priority].n, PRI_STACK_LENGTH,
gPriStack[priority].names[gPriStack[priority].n - 1].c_str());
DisplayQueue();
} else {
printf("[OVERLORD ISO QUEUE] Failed to queue!\n");
@@ -194,31 +195,31 @@ u32 QueueMessage(IsoMessage *cmd, int32_t priority, const char *name) {
/*!
* Remove a message from the priority stack.
*/
void UnqueueMessage(IsoMessage *cmd) {
void UnqueueMessage(IsoMessage* cmd) {
int pri = 0;
u32 idx = 0;
PriStackEntry* pse;
// loop over priorities
for(pri = 0; pri < N_PRIORITIES; pri++) {
for (pri = 0; pri < N_PRIORITIES; pri++) {
pse = gPriStack + pri;
// loop over entries
for(idx = 0; idx < gPriStack[pri].n; idx++) {
if(pse->cmds[idx] == cmd) {
for (idx = 0; idx < gPriStack[pri].n; idx++) {
if (pse->cmds[idx] == cmd) {
goto found;
}
}
}
printf("[OVERLORD ISO QUEUE] Failed to unqueue!\n");
found:
found:
assert(gPriStack[pri].cmds[idx] == cmd);
// pop
gPriStack[pri].n--;
// and move other entries up.
while(idx < gPriStack[pri].n) {
while (idx < gPriStack[pri].n) {
pse->cmds[idx] = pse->cmds[idx + 1];
idx++;
}
@@ -227,33 +228,32 @@ void UnqueueMessage(IsoMessage *cmd) {
/*!
* Get the highest priority message with an open buffer.
* (Note - messages with priority less than max priority will be gotten if they have < 2 buffers filled)
* (Note - messages with priority less than max priority will be gotten if they have < 2 buffers
* filled)
* @return
*/
IsoMessage* GetMessage() {
// loop over all priorities
for(int pri = (N_PRIORITIES - 1); pri >= 0; pri--) {
for (int pri = (N_PRIORITIES - 1); pri >= 0; pri--) {
auto pse = gPriStack + pri;
int idx = gPriStack[pri].n;
for(idx = idx - 1; idx >= 0; idx--) {
if(pse->cmds[idx]->fd &&
pse->cmds[idx]->status == CMD_STATUS_IN_PROGRESS &&
pse->cmds[idx]->ready_for_data) {
if(pri == N_PRIORITIES - 1) {
for (idx = idx - 1; idx >= 0; idx--) {
if (pse->cmds[idx]->fd && pse->cmds[idx]->status == CMD_STATUS_IN_PROGRESS &&
pse->cmds[idx]->ready_for_data) {
if (pri == N_PRIORITIES - 1) {
// return high priority commands only if they don't have any buffers filled
if(!pse->cmds[idx]->callback_buffer) {
if (!pse->cmds[idx]->callback_buffer) {
return pse->cmds[idx];
}
} else {
// return lower priority commands if they don't have 2 buffers filled.
if(!pse->cmds[idx]->callback_buffer ||
!(IsoBufferHeader*)(pse->cmds[idx]->callback_buffer)->next) {
if (!pse->cmds[idx]->callback_buffer ||
!(IsoBufferHeader*)(pse->cmds[idx]->callback_buffer)->next) {
return pse->cmds[idx];
}
}
}
}
}
return nullptr;
}
@@ -265,21 +265,27 @@ void ProcessMessageData() {
int32_t pri = N_PRIORITIES - 1;
for (;;) {
if (pri < 0) return;
if (pri < 0)
return;
int32_t cmdID = gPriStack[pri].n;
IsoMessage *popped_command;
IsoMessage* popped_command;
do {
cmdID--;
if (cmdID < 0) goto end_cur;
if (cmdID < 0)
goto end_cur;
popped_command = gPriStack[pri].cmds[cmdID];
auto* callback_buffer = popped_command->callback_buffer;
if(popped_command->status == CMD_STATUS_IN_PROGRESS && callback_buffer) { // if we have a callback buffer (meaning a read finished and let us know)
if (popped_command->status == CMD_STATUS_IN_PROGRESS &&
callback_buffer) { // if we have a callback buffer (meaning a read finished and let us
// know)
// execute the callback!
uint32_t callback_result = popped_command->callback_function(popped_command, callback_buffer);
uint32_t callback_result =
popped_command->callback_function(popped_command, callback_buffer);
popped_command->status = callback_result;
// printf("ProcessMessage Data set command %p status to %d\n", popped_command, popped_command->status);
// printf("ProcessMessage Data set command %p status to %d\n", popped_command,
// popped_command->status);
// if we're done with the buffer, free it and load the next one (if there is one)
if(callback_buffer->data_size == 0) {
if (callback_buffer->data_size == 0) {
popped_command->callback_buffer = (IsoBufferHeader*)callback_buffer->next;
printf("free 1\n");
FreeBuffer(callback_buffer);
@@ -290,7 +296,7 @@ void ProcessMessageData() {
ReturnMessage(popped_command);
// return message todo this will free vag commands!
pri++;
end_cur:
end_cur:
pri--;
}
}
@@ -298,9 +304,9 @@ void ProcessMessageData() {
/*!
* Wakeup thread/message mbx for a message
*/
void ReturnMessage(IsoMessage *cmd) {
if(!cmd->messagebox_to_reply) {
if(cmd->thread_id == 0) {
void ReturnMessage(IsoMessage* cmd) {
if (!cmd->messagebox_to_reply) {
if (cmd->thread_id == 0) {
FreeVAGCommand((VagCommand*)cmd);
} else {
WakeupThread(cmd->thread_id);
@@ -313,17 +319,17 @@ void ReturnMessage(IsoMessage *cmd) {
/*!
* Free buffers, close files, and remove from priority stack
*/
void ReleaseMessage(IsoMessage *cmd) {
void ReleaseMessage(IsoMessage* cmd) {
// kill all buffers
while(cmd->callback_buffer) {
while (cmd->callback_buffer) {
auto old_head = cmd->callback_buffer;
cmd->callback_buffer = (IsoBufferHeader*)old_head->next;
printf("free 2\n");
printf("free 2\n");
FreeBuffer(old_head);
}
// close file
if(cmd->fd) {
if (cmd->fd) {
isofs->close(cmd->fd);
}
@@ -333,23 +339,22 @@ void ReleaseMessage(IsoMessage *cmd) {
// GetVAGCommand
VagCommand* GetVAGCommand() {
for(;;) {
for (;;) {
// wait for command to be available
while(vag_cmd_cnt == (N_VAG_CMDS - 1)) {
while (vag_cmd_cnt == (N_VAG_CMDS - 1)) {
DelayThread(100);
}
// wait for VAG semaphore
while(WaitSema(sSema)) {
while (WaitSema(sSema)) {
}
// try to get something.
for(s32 i = 0; i < N_VAG_CMDS; i++) {
if(!((vag_cmd_used >> (i & 0x1f)) & 1)) {
for (s32 i = 0; i < N_VAG_CMDS; i++) {
if (!((vag_cmd_used >> (i & 0x1f)) & 1)) {
// free!
vag_cmd_used |= (1 << (i & 0x1f));
vag_cmd_cnt++;
if(vag_cmd_cnt > max_vag_cmd_cnt) {
if (vag_cmd_cnt > max_vag_cmd_cnt) {
max_vag_cmd_cnt = vag_cmd_cnt;
}
SignalSema(sSema);
@@ -363,9 +368,8 @@ VagCommand* GetVAGCommand() {
void FreeVAGCommand(VagCommand* cmd) {
s32 idx = cmd - vag_cmds;
if(idx >= 0 && idx < N_VAG_CMDS && ((vag_cmd_used >> (idx & 0x1f)) & 1)) {
while(WaitSema(sSema)) {
if (idx >= 0 && idx < N_VAG_CMDS && ((vag_cmd_used >> (idx & 0x1f)) & 1)) {
while (WaitSema(sSema)) {
}
vag_cmd_used &= ~(1 << (idx & 0x1f));
+5 -9
View File
@@ -1,21 +1,17 @@
#ifndef JAK_V2_ISO_QUEUE_H
#define JAK_V2_ISO_QUEUE_H
#include "common/common_types.h"
#include "isocommon.h"
void iso_queue_init_globals();
void InitBuffers();
IsoBufferHeader* AllocateBuffer(uint32_t size);
void FreeBuffer(IsoBufferHeader *buffer);
u32 QueueMessage(IsoMessage *cmd, int32_t priority, const char *name);
void UnqueueMessage(IsoMessage *cmd);
void FreeBuffer(IsoBufferHeader* buffer);
u32 QueueMessage(IsoMessage* cmd, int32_t priority, const char* name);
void UnqueueMessage(IsoMessage* cmd);
IsoMessage* GetMessage();
void ProcessMessageData();
void ReturnMessage(IsoMessage *cmd);
void ReturnMessage(IsoMessage* cmd);
#endif //JAK_V2_ISO_QUEUE_H
#endif // JAK_V2_ISO_QUEUE_H
+26 -27
View File
@@ -12,7 +12,7 @@ int start_overlord(int argc, const char* const* argv) {
(void)argc;
FlushDcache();
CpuEnableIntr();
if(!sceSifCheckInit()) {
if (!sceSifCheckInit()) {
sceSifInit();
}
@@ -20,7 +20,7 @@ int start_overlord(int argc, const char* const* argv) {
InitBanks();
InitSound_Overlord();
InitRamdisk();
// RegisterVblankHandler(0, 0x20, VBlank_Handler, nullptr);
// RegisterVblankHandler(0, 0x20, VBlank_Handler, nullptr);
ThreadParam thread_param;
thread_param.attr = TH_C;
@@ -28,37 +28,37 @@ int start_overlord(int argc, const char* const* argv) {
thread_param.stackSize = 0x800;
thread_param.option = 0;
thread_param.entry = (void*)Thread_Server;
strcpy(thread_param.name, "Server"); // added
strcpy(thread_param.name, "Server"); // added
auto thread_server = CreateThread(&thread_param);
if(thread_server <= 0) {
if (thread_server <= 0) {
return 1;
}
// thread_param.attr = TH_C;
// thread_param.initPriority = 96;
// thread_param.stackSize = 0x800;
// thread_param.option = 0;
// thread_param.entry = Thread_Player;
// auto thread_player = CreateThread(&thread_param);
// if(thread_player <= 0) {
// return 1;
// }
//
// thread_param.attr = TH_C;
// thread_param.initPriority = 99;
// thread_param.stackSize = 0x1000;
// thread_param.option = 0;
// thread_param.entry = Thread_Loader;
// auto thread_loader = CreateThread(&thread_param);
// if(thread_loader <= 0) {
// return 1;
// }
// thread_param.attr = TH_C;
// thread_param.initPriority = 96;
// thread_param.stackSize = 0x800;
// thread_param.option = 0;
// thread_param.entry = Thread_Player;
// auto thread_player = CreateThread(&thread_param);
// if(thread_player <= 0) {
// return 1;
// }
//
// thread_param.attr = TH_C;
// thread_param.initPriority = 99;
// thread_param.stackSize = 0x1000;
// thread_param.option = 0;
// thread_param.entry = Thread_Loader;
// auto thread_loader = CreateThread(&thread_param);
// if(thread_loader <= 0) {
// return 1;
// }
InitISOFS(argv[1], argv[2]);
StartThread(thread_server, 0);
// StartThread(thread_player, 0);
// StartThread(thread_loader, 0);
// StartThread(thread_player, 0);
// StartThread(thread_loader, 0);
return 0;
}
@@ -66,7 +66,6 @@ int start_overlord(int argc, const char* const* argv) {
* Loop endlessly and never return.
*/
void ExitIOP() {
while(true) {
while (true) {
}
}
+1 -1
View File
@@ -4,4 +4,4 @@
int start_overlord(int argc, const char* const* argv);
void ExitIOP();
#endif //JAK_V2_OVERLORD_H
#endif // JAK_V2_OVERLORD_H
+1 -3
View File
@@ -1,5 +1,3 @@
#include "sbank.h"
void InitBanks() {
}
void InitBanks() {}
+1 -1
View File
@@ -3,4 +3,4 @@
void InitBanks();
#endif //JAK_V2_SBANK_H
#endif // JAK_V2_SBANK_H
-1
View File
@@ -1,7 +1,6 @@
#include <assert.h>
#include "soundcommon.h"
void PrintBankInfo(void* buffer) {
(void)buffer;
assert(false);
+1 -1
View File
@@ -3,4 +3,4 @@
void PrintBankInfo(void* buffer);
#endif //JAK_V2_SOUNDCOMMON_H
#endif // JAK_V2_SOUNDCOMMON_H
+1 -1
View File
@@ -8,4 +8,4 @@ void srpc_init_globals();
constexpr int MUSIC_TWEAK_SIZE = 0x204;
extern u8 gMusicTweakInfo[MUSIC_TWEAK_SIZE];
#endif //JAK_V2_SRPC_H
#endif // JAK_V2_SRPC_H
+1 -3
View File
@@ -1,5 +1,3 @@
#include "ssound.h"
void InitSound_Overlord() {
}
void InitSound_Overlord() {}
+1 -1
View File
@@ -3,4 +3,4 @@
void InitSound_Overlord();
#endif //JAK_V2_SSOUND_H
#endif // JAK_V2_SSOUND_H
+1 -1
View File
@@ -5,4 +5,4 @@
u32 STRThread();
u32 PLAYThread();
#endif //JAK_V2_STREAM_H
#endif // JAK_V2_STREAM_H
-1
View File
@@ -10,7 +10,6 @@
class Deci2Server;
namespace ee {
void LIBRARY_INIT_sceDeci2();
+19 -17
View File
@@ -31,22 +31,19 @@ void sceSifInitRpc(int mode) {
* Flush Data Cache
*/
void FlushDcache() {
// Do nothing! The data cache does not need to be flushed on x86 as we have no DMA which bypasses cache.
// Do nothing! The data cache does not need to be flushed on x86 as we have no DMA which bypasses
// cache.
}
/*!
* Enable CPU Interrupts
*/
void CpuDisableIntr() {
}
void CpuDisableIntr() {}
/*!
* Disable CPU Interrupts
*/
void CpuEnableIntr() {
}
void CpuEnableIntr() {}
namespace {
::IOP* iop;
@@ -68,15 +65,15 @@ void LIBRARY_kill() {
* How much free memory is there, in bytes?
*/
int QueryTotalFreeMemSize() {
// this value is somewhat arbitrary - it's a lot, but not enough to make OVERLORD think it is running on
// an 8MB-of-IOP-RAM development machine.
// this value is somewhat arbitrary - it's a lot, but not enough to make OVERLORD think it is
// running on an 8MB-of-IOP-RAM development machine.
return 0x100000;
}
/*!
* Allocate memory.
*/
void *AllocSysMemory(int type, unsigned long size, void *addr) {
* Allocate memory.
*/
void* AllocSysMemory(int type, unsigned long size, void* addr) {
assert(type == SMEM_Low);
assert(addr == nullptr);
return iop->iop_alloc(size);
@@ -112,8 +109,13 @@ void sceSifSetRpcQueue(sceSifQueueData* dq, int key) {
iop->kernel.set_rpc_queue(dq, key);
}
void sceSifRegisterRpc(sceSifServeData* serve, unsigned int request,
sceSifRpcFunc func, void* buff, sceSifRpcFunc cfunc, void* cbuff, sceSifQueueData* qd) {
void sceSifRegisterRpc(sceSifServeData* serve,
unsigned int request,
sceSifRpcFunc func,
void* buff,
sceSifRpcFunc cfunc,
void* cbuff,
sceSifQueueData* qd) {
serve->command = request;
serve->func = func;
serve->buff = buff;
@@ -140,11 +142,11 @@ int sceCdSync(int mode) {
}
int sceCdGetError() {
return 0; // no error
return 0; // no error
}
int sceCdGetDiskType() {
return SCECdPS2DVD; // always a DVD (for now)
return SCECdPS2DVD; // always a DVD (for now)
}
int sceCdMmode(int media) {
@@ -213,4 +215,4 @@ s32 WakeupThread(s32 thid) {
iop->kernel.WakeupThread(thid);
return 0;
}
}
} // namespace iop
+29 -26
View File
@@ -3,35 +3,34 @@
#include "common/common_types.h"
#define SMEM_Low (0)
#define SMEM_Low (0)
#define SMEM_High (1)
#define SMEM_Addr (2)
#define SCECdCD 1
#define SCECdDVD 2
#define SCECdIllgalMedia 0xff
#define SCECdIllegalMedia 0xff
#define SCECdDVDV 0xfe
#define SCECdCDDA 0xfd
#define SCECdPS2DVD 0x14
#define SCECdPS2CD 0x12
#define SCECdDETCT 0x01
#define SCECdCD 1
#define SCECdDVD 2
#define SCECdIllgalMedia 0xff
#define SCECdIllegalMedia 0xff
#define SCECdDVDV 0xfe
#define SCECdCDDA 0xfd
#define SCECdPS2DVD 0x14
#define SCECdPS2CD 0x12
#define SCECdDETCT 0x01
#define SCECdComplete 0x02
#define SCECdNotReady 0x06
#define KE_MBOX_NOMSG -424
#define TH_C 0x02000000
#define TH_C 0x02000000
class IOP;
namespace iop {
typedef void * (* sceSifRpcFunc)(unsigned int,void *,int);
typedef void* (*sceSifRpcFunc)(unsigned int, void*, int);
struct sceSifServeData {
unsigned int command; // the RPC ID
unsigned int command; // the RPC ID
sceSifRpcFunc func;
void* buff;
};
@@ -48,14 +47,13 @@ struct sceCdRMode {
uint8_t pad;
};
struct sceSifDmaData{
void* data;
void* addr;
unsigned int size;
unsigned int mode;
struct sceSifDmaData {
void* data;
void* addr;
unsigned int size;
unsigned int mode;
};
struct SysClock {
uint32_t hi, lo;
};
@@ -72,7 +70,7 @@ struct MbxParam {
struct ThreadParam {
u32 attr;
u32 option;
void *entry;
void* entry;
int stackSize;
int initPriority;
@@ -87,9 +85,9 @@ struct SemaParam {
uint32_t option;
};
//void PS2_RegisterIOP(IOP *iop);
// void PS2_RegisterIOP(IOP *iop);
int QueryTotalFreeMemSize();
void *AllocSysMemory(int type, unsigned long size, void *addr);
void* AllocSysMemory(int type, unsigned long size, void* addr);
int GetThreadId();
void CpuDisableIntr();
@@ -103,8 +101,13 @@ s32 WakeupThread(s32 thid);
void sceSifInitRpc(int mode);
void sceSifInitRpc(unsigned int mode);
void sceSifSetRpcQueue(sceSifQueueData* dq, int key);
void sceSifRegisterRpc(sceSifServeData* serve, unsigned int request,
sceSifRpcFunc func, void* buff, sceSifRpcFunc cfunc, void* cbuff, sceSifQueueData* qd);
void sceSifRegisterRpc(sceSifServeData* serve,
unsigned int request,
sceSifRpcFunc func,
void* buff,
sceSifRpcFunc cfunc,
void* cbuff,
sceSifQueueData* qd);
void sceSifRpcLoop(sceSifQueueData* pd);
int sceCdRead(uint32_t logical_sector, uint32_t sectors, void* buf, sceCdRMode* mode);
@@ -135,6 +138,6 @@ void sceSifInit();
void LIBRARY_INIT();
void LIBRARY_register(::IOP* i);
void LIBRARY_kill();
}
} // namespace iop
#endif // JAK1_IOP_H
+5 -5
View File
@@ -11,7 +11,7 @@ namespace ee {
namespace {
// CD/DVD media type set by sceCdMMode
int media_mode;
}
} // namespace
void LIBRARY_INIT_sceCd() {
media_mode = -1;
@@ -21,9 +21,9 @@ void LIBRARY_INIT_sceCd() {
* Initialize the CD/DVD subsystem.
* init_mode should be SCECdINIT
*/
int sceCdInit(int init_mode){
int sceCdInit(int init_mode) {
assert(init_mode == SCECdINIT);
return 1; // Initialization was performed normally
return 1; // Initialization was performed normally
}
/*!
@@ -31,7 +31,7 @@ int sceCdInit(int init_mode){
*/
int sceCdMmode(int media) {
media_mode = media;
return 1; // If successful, returns 1
return 1; // If successful, returns 1
}
/*!
@@ -49,7 +49,7 @@ int sceCdDiskReady(int mode) {
*/
int sceCdGetDiskType() {
// if we set CD or DVD, return the appropriate PS2 game disk type.
switch(media_mode) {
switch (media_mode) {
case SCECdCD:
return SCECdPS2CD;
case SCECdDVD:
+1 -1
View File
@@ -8,4 +8,4 @@ int sceScfGetAspect() {
int sceScfGetLanguage() {
return SCE_ENGLISH_LANGUAGE;
}
}
} // namespace ee
+1 -1
View File
@@ -26,6 +26,6 @@ int sceScfGetAspect();
* Return a SONY SCE_LANGUAGE value, which differs from GOAL.
*/
int sceScfGetLanguage();
}
} // namespace ee
#endif // JAK1_LIBSCF_H
+19 -12
View File
@@ -6,7 +6,7 @@
namespace ee {
namespace {
namespace {
::IOP* iop;
}
@@ -30,17 +30,16 @@ int sceSifSyncIop() {
return 1;
}
void sceFsReset() {
}
void sceFsReset() {}
int sceSifLoadModule(const char* name, int arg_size, const char* args) {
if(!strcmp(name, "cdrom0:\\\\DRIVERS\\\\OVERLORD.IRX;1") || !strcmp(name, "host0:binee/overlord.irx")) {
if (!strcmp(name, "cdrom0:\\\\DRIVERS\\\\OVERLORD.IRX;1") ||
!strcmp(name, "host0:binee/overlord.irx")) {
const char* src = args;
char* dst = iop->overlord_arg_data;
int cnt;
iop->overlord_argv[0] = nullptr;
for(cnt = 1; src - args < arg_size; cnt++) {
for (cnt = 1; src - args < arg_size; cnt++) {
auto len = strlen(src);
memcpy(dst, src, len + 1);
iop->overlord_argv[cnt] = dst;
@@ -49,8 +48,8 @@ int sceSifLoadModule(const char* name, int arg_size, const char* args) {
}
iop->overlord_argc = cnt;
for(int i = 0; i < cnt; i++) {
if(iop->overlord_argv[i])
for (int i = 0; i < cnt; i++) {
if (iop->overlord_argv[i])
printf("arg %d : %s\n", i, iop->overlord_argv[i]);
}
iop->set_ee_main_mem(g_ee_main_mem);
@@ -65,10 +64,18 @@ int sceMcInit() {
return 1;
}
s32 sceSifCallRpc(sceSifClientData* bd, u32 fno, u32 mode, void* send, s32 ssize, void* recv, s32 rsize, void* end_func, void* end_para) {
s32 sceSifCallRpc(sceSifClientData* bd,
u32 fno,
u32 mode,
void* send,
s32 ssize,
void* recv,
s32 rsize,
void* end_func,
void* end_para) {
assert(!end_func);
assert(!end_para);
assert(mode == 1); // async
assert(mode == 1); // async
iop->kernel.sif_rpc(bd->rpcd.id, fno, mode, send, ssize, recv, rsize);
return 0;
}
@@ -79,9 +86,9 @@ s32 sceSifCheckStatRpc(sceSifRpcData* bd) {
}
s32 sceSifBindRpc(sceSifClientData* bd, u32 request, u32 mode) {
assert(mode == 1); // async
assert(mode == 1); // async
bd->rpcd.id = request;
bd->serve = (sceSifServeData*)1;
return 0;
}
}
} // namespace ee
+18 -11
View File
@@ -16,17 +16,16 @@ struct sceSifServeData {
};
struct sceSifClientData {
sceSifRpcData rpcd;
// unsigned int command;
void *buff;
void *gp;
// sceSifEndFunc func;
void *para;
// struct _sif_serve_data *serve;
sceSifServeData *serve;
sceSifRpcData rpcd;
// unsigned int command;
void* buff;
void* gp;
// sceSifEndFunc func;
void* para;
// struct _sif_serve_data *serve;
sceSifServeData* serve;
};
void LIBRARY_sceSif_register(::IOP* i);
void LIBRARY_INIT_sceSif();
@@ -36,9 +35,17 @@ int sceSifSyncIop();
void sceFsReset();
int sceSifLoadModule(const char* name, int arg_size, const char* args);
int sceMcInit();
s32 sceSifCallRpc(sceSifClientData* bd, u32 fno, u32 mode, void* send, s32 ssize, void* recv, s32 rsize, void* end_func, void* end_para);
s32 sceSifCallRpc(sceSifClientData* bd,
u32 fno,
u32 mode,
void* send,
s32 ssize,
void* recv,
s32 rsize,
void* end_func,
void* end_para);
s32 sceSifCheckStatRpc(sceSifRpcData* bd);
s32 sceSifBindRpc(sceSifClientData* bd, u32 request, u32 mode);
}
} // namespace ee
#endif // JAK1_SIF_EE_H
+5 -5
View File
@@ -3,7 +3,7 @@
#include "stubs.h"
namespace ee {
s32 sceOpen(const char *filename, s32 flag) {
s32 sceOpen(const char* filename, s32 flag) {
(void)filename;
(void)flag;
throw std::runtime_error("sceOpen NYI");
@@ -14,14 +14,14 @@ s32 sceClose(s32 fd) {
throw std::runtime_error("sceClose NYI");
}
s32 sceRead(s32 fd, void *buf, s32 nbyte) {
s32 sceRead(s32 fd, void* buf, s32 nbyte) {
(void)fd;
(void)buf;
(void)nbyte;
throw std::runtime_error("sceRead NYI");
}
s32 sceWrite(s32 fd, const void *buf, s32 nbyte) {
s32 sceWrite(s32 fd, const void* buf, s32 nbyte) {
(void)fd;
(void)buf;
(void)nbyte;
@@ -79,7 +79,7 @@ void FlushCache() {
assert(false);
}
}
} // namespace ee
namespace iop {
u32 snd_BankLoadByLoc(u32 sector, u32 unk) {
@@ -98,4 +98,4 @@ void snd_ResolveBankXREFS() {
assert(false);
}
}
} // namespace iop
+18 -19
View File
@@ -4,33 +4,33 @@
#include "common/common_types.h"
#ifndef SCE_SEEK_SET
#define SCE_SEEK_SET (0)
#define SCE_SEEK_SET (0)
#endif
#ifndef SCE_SEEK_CUR
#define SCE_SEEK_CUR (1)
#define SCE_SEEK_CUR (1)
#endif
#ifndef SCE_SEEK_END
#define SCE_SEEK_END (2)
#define SCE_SEEK_END (2)
#endif
#define SCE_RDONLY 0x0001
#define SCE_WRONLY 0x0002
#define SCE_RDWR 0x0003
#define SCE_NBLOCK 0x0010
#define SCE_APPEND 0x0100
#define SCE_CREAT 0x0200
#define SCE_TRUNC 0x0400
#define SCE_EXCL 0x0800
#define SCE_NOBUF 0x4000
#define SCE_NOWAIT 0x8000
#define SCE_RDONLY 0x0001
#define SCE_WRONLY 0x0002
#define SCE_RDWR 0x0003
#define SCE_NBLOCK 0x0010
#define SCE_APPEND 0x0100
#define SCE_CREAT 0x0200
#define SCE_TRUNC 0x0400
#define SCE_EXCL 0x0800
#define SCE_NOBUF 0x4000
#define SCE_NOWAIT 0x8000
#define SCE_PAD_DMA_BUFFER_SIZE 0x100
namespace ee {
s32 sceOpen(const char *filename, s32 flag);
s32 sceOpen(const char* filename, s32 flag);
s32 sceClose(s32 fd);
s32 sceRead(s32 fd, void *buf, s32 nbyte);
s32 sceWrite(s32 fd, const void *buf, s32 nbyte);
s32 sceRead(s32 fd, void* buf, s32 nbyte);
s32 sceWrite(s32 fd, const void* buf, s32 nbyte);
s32 sceLseek(s32 fd, s32 offset, s32 where);
void sceGsSyncV();
void sceGsSyncPath();
@@ -42,13 +42,12 @@ void sceGsGetIMR();
void sceGsExecStoreImage();
void FlushCache();
int scePadPortOpen(int port, int slot, void* data);
}
} // namespace ee
namespace iop {
u32 snd_BankLoadByLoc(u32 sector, u32 unk);
u32 snd_GetLastLoadError();
void snd_ResolveBankXREFS();
}
} // namespace iop
#endif // JAK1_STUBS_H
+2 -5
View File
@@ -30,14 +30,13 @@ class Deci2Server {
void run();
private:
void accept_thread_func();
bool kill_accept_thread = false;
char* buffer = nullptr;
int server_fd;
int server_fd = -1;
sockaddr_in addr;
int new_sock;
int new_sock = -1;
bool server_initialized = false;
bool accept_thread_running = false;
bool server_connected = false;
@@ -51,8 +50,6 @@ class Deci2Server {
int* d2_driver_count = nullptr;
};
#endif // JAK1_DECI2SERVER_H
*/
+59 -50
View File
@@ -7,16 +7,18 @@
* Create a new thread. Will not run the thread.
*/
s32 IOP_Kernel::CreateThread(std::string name, u32 (*func)()) {
if(_currentThread != -1) throw std::runtime_error("tried to create thread from thread");
if (_currentThread != -1)
throw std::runtime_error("tried to create thread from thread");
u32 ID = (u32)_nextThID++;
if(threads.size() != ID) throw std::runtime_error("thread number error?");
if (threads.size() != ID)
throw std::runtime_error("thread number error?");
// add entry
threads.emplace_back(name, func, ID, this);
// setup the thread!
// printf("[IOP Kernel] SetupThread %s...\n", name.c_str());
// hack to allow creating a "null thread" which doesn't/can't run but occupies slot 0.
if(func) {
if (func) {
_currentThread = ID;
// create OS thread, will run the setupThread function
threads.back().thread = new std::thread(&IOP_Kernel::setupThread, this, ID);
@@ -26,7 +28,6 @@ s32 IOP_Kernel::CreateThread(std::string name, u32 (*func)()) {
_currentThread = -1;
}
return ID;
}
@@ -34,8 +35,8 @@ s32 IOP_Kernel::CreateThread(std::string name, u32 (*func)()) {
* Start a thread. Runs it once, then marks it to run on each dispatch of the IOP kernel.
*/
void IOP_Kernel::StartThread(s32 id) {
threads.at(id).started = true; // mark for run
runThread(id); // run now
threads.at(id).started = true; // mark for run
runThread(id); // run now
}
/*!
@@ -46,7 +47,7 @@ void IOP_Kernel::setupThread(s32 id) {
returnToKernel();
threads.at(id).waitForDispatch();
// printf("[IOP Kernel] Thread %s first dispatch!\n", threads.at(id).name.c_str());
if(_currentThread != id) {
if (_currentThread != id) {
throw std::runtime_error("the wrong thread has run!\n");
}
(threads.at(id).function)();
@@ -59,7 +60,8 @@ void IOP_Kernel::setupThread(s32 id) {
* Run a thread (call from kernel)
*/
void IOP_Kernel::runThread(s32 id) {
if(_currentThread != -1) throw std::runtime_error("tried to runThread in a thread");
if (_currentThread != -1)
throw std::runtime_error("tried to runThread in a thread");
_currentThread = id;
threads.at(id).dispatch();
threads.at(id).waitForReturnToKernel();
@@ -75,7 +77,7 @@ void IOP_Kernel::SuspendThread() {
s32 oldThread = getCurrentThread();
threads.at(oldThread).returnToKernel();
threads.at(oldThread).waitForDispatch();
if(_currentThread != oldThread) {
if (_currentThread != oldThread) {
throw std::runtime_error("bad resume");
}
}
@@ -84,9 +86,9 @@ void IOP_Kernel::SuspendThread() {
* Sleep a thread. Must be explicitly woken up.
*/
void IOP_Kernel::SleepThread() {
if(getCurrentThread() == -1) {
if (getCurrentThread() == -1) {
mainThreadSleep = true;
while(mainThreadSleep) {
while (mainThreadSleep) {
dispatchAll();
}
} else {
@@ -99,7 +101,7 @@ void IOP_Kernel::SleepThread() {
* Wake up a thread. Doesn't run it immediately though.
*/
void IOP_Kernel::WakeupThread(s32 id) {
if(id == -1) {
if (id == -1) {
mainThreadSleep = false;
} else {
threads.at(id).started = true;
@@ -111,14 +113,14 @@ void IOP_Kernel::WakeupThread(s32 id) {
* Dispatch all IOP threads.
*/
void IOP_Kernel::dispatchAll() {
for(u64 i = 0; i < threads.size(); i++) {
if(threads[i].started && !threads[i].done) {
// printf("[IOP Kernel] Dispatch %s (%ld)\n", threads[i].name.c_str(), i);
for (u64 i = 0; i < threads.size(); i++) {
if (threads[i].started && !threads[i].done) {
// printf("[IOP Kernel] Dispatch %s (%ld)\n", threads[i].name.c_str(), i);
_currentThread = i;
threads[i].dispatch();
threads[i].waitForReturnToKernel();
_currentThread = -1;
//printf("[IOP Kernel] back to kernel!\n");
// printf("[IOP Kernel] back to kernel!\n");
}
}
}
@@ -128,7 +130,8 @@ void IOP_Kernel::dispatchAll() {
*/
void IopThreadRecord::returnToKernel() {
runThreadReady = false;
if(kernel->getCurrentThread() != thID) throw std::runtime_error("tried to sleep the wrong thread!");
if (kernel->getCurrentThread() != thID)
throw std::runtime_error("tried to sleep the wrong thread!");
{
std::lock_guard<std::mutex> lck(*threadToKernelMutex);
@@ -142,7 +145,8 @@ void IopThreadRecord::returnToKernel() {
*/
void IopThreadRecord::dispatch() {
syscallReady = false;
if(kernel->getCurrentThread() != thID) throw std::runtime_error("tried to dispatch the wrong thread!");
if (kernel->getCurrentThread() != thID)
throw std::runtime_error("tried to dispatch the wrong thread!");
{
std::lock_guard<std::mutex> lck(*kernelToThreadMutex);
runThreadReady = true;
@@ -155,22 +159,22 @@ void IopThreadRecord::dispatch() {
*/
void IopThreadRecord::waitForReturnToKernel() {
std::unique_lock<std::mutex> lck(*threadToKernelMutex);
threadToKernelCV->wait(lck, [this]{return syscallReady;});
// syscallReady = false;
threadToKernelCV->wait(lck, [this] { return syscallReady; });
// syscallReady = false;
}
/*!
* Thread waits for kernel to dispatch it.
*/
void IopThreadRecord::waitForDispatch() {
//if(kernel->getCurrentThread() == -1) throw std::runtime_error("tried to suspend main!\n");
// if(kernel->getCurrentThread() == -1) throw std::runtime_error("tried to suspend main!\n");
std::unique_lock<std::mutex> lck(*kernelToThreadMutex);
kernelToThreadCV->wait(lck, [this]{return runThreadReady;});
//runThreadReady = false;
kernelToThreadCV->wait(lck, [this] { return runThreadReady; });
// runThreadReady = false;
}
void IOP_Kernel::set_rpc_queue(iop::sceSifQueueData *qd, u32 thread) {
for(const auto& r : sif_records) {
void IOP_Kernel::set_rpc_queue(iop::sceSifQueueData* qd, u32 thread) {
for (const auto& r : sif_records) {
assert(!(r.qd == qd || r.thread_to_wake == thread));
}
SifRecord rec;
@@ -179,14 +183,14 @@ void IOP_Kernel::set_rpc_queue(iop::sceSifQueueData *qd, u32 thread) {
sif_records.push_back(rec);
}
typedef void * (* sif_rpc_handler)(unsigned int,void *,int);
typedef void* (*sif_rpc_handler)(unsigned int, void*, int);
bool IOP_Kernel::sif_busy(u32 id) {
sif_mtx.lock();
bool rv = false;
bool found = false;
for(auto& r : sif_records) {
if(r.qd->serve_data->command == id) {
for (auto& r : sif_records) {
if (r.qd->serve_data->command == id) {
rv = !r.cmd.finished;
found = true;
break;
@@ -197,14 +201,19 @@ bool IOP_Kernel::sif_busy(u32 id) {
return rv;
}
void IOP_Kernel::sif_rpc(s32 rpcChannel, u32 fno, bool async, void *sendBuff, s32 sendSize, void *recvBuff,
void IOP_Kernel::sif_rpc(s32 rpcChannel,
u32 fno,
bool async,
void* sendBuff,
s32 sendSize,
void* recvBuff,
s32 recvSize) {
assert(async);
sif_mtx.lock();
// step 1 - find entry
SifRecord* rec = nullptr;
for(auto& e : sif_records) {
if(e.qd->serve_data->command == (u32)rpcChannel) {
for (auto& e : sif_records) {
if (e.qd->serve_data->command == (u32)rpcChannel) {
rec = &e;
}
}
@@ -229,15 +238,15 @@ void IOP_Kernel::sif_rpc(s32 rpcChannel, u32 fno, bool async, void *sendBuff, s3
}
void IOP_Kernel::rpc_loop(iop::sceSifQueueData* qd) {
while(true) {
while (true) {
bool got_cmd = false;
SifRpcCommand cmd;
sif_rpc_handler func = nullptr;
// get command and mark it as started if we get it
sif_mtx.lock();
for(auto& r : sif_records) {
if(r.qd == qd) {
for (auto& r : sif_records) {
if (r.qd == qd) {
cmd = r.cmd;
got_cmd = true;
r.cmd.started = true;
@@ -247,41 +256,40 @@ void IOP_Kernel::rpc_loop(iop::sceSifQueueData* qd) {
sif_mtx.unlock();
// handle command
if(got_cmd) {
if(cmd.shutdown_now) {
if (got_cmd) {
if (cmd.shutdown_now) {
return;
}
if(!cmd.started) {
if (!cmd.started) {
// cf
assert(func);
auto data = func(cmd.fno, cmd.buff, cmd.size);
if(cmd.copy_back_buff && cmd.copy_back_size) {
if (cmd.copy_back_buff && cmd.copy_back_size) {
memcpy(cmd.copy_back_buff, data, cmd.copy_back_size);
}
sif_mtx.lock();
for(auto& r : sif_records) {
if(r.qd == qd) {
for (auto& r : sif_records) {
if (r.qd == qd) {
assert(r.cmd.started);
r.cmd.finished = true;
}
}
sif_mtx.unlock();
}
}
SuspendThread();
}
}
void IOP_Kernel::read_disc_sectors(u32 sector, u32 sectors, void *buffer) {
if(!iso_disc_file) {
void IOP_Kernel::read_disc_sectors(u32 sector, u32 sectors, void* buffer) {
if (!iso_disc_file) {
iso_disc_file = fopen("./disc.iso", "rb");
}
assert(iso_disc_file);
if(fseek(iso_disc_file, sector * 0x800, SEEK_SET)) {
if (fseek(iso_disc_file, sector * 0x800, SEEK_SET)) {
assert(false);
}
auto rv = fread(buffer, sectors * 0x800, 1, iso_disc_file);
@@ -290,17 +298,18 @@ void IOP_Kernel::read_disc_sectors(u32 sector, u32 sectors, void *buffer) {
void IOP_Kernel::shutdown() {
// shutdown most threads
for(auto& r : sif_records) {
for (auto& r : sif_records) {
r.cmd.shutdown_now = true;
}
for(auto& t : threads) {
for (auto& t : threads) {
t.wantExit = true;
}
for(auto& t : threads) {
if(t.thID == 0) continue;
while(!t.done) {
for (auto& t : threads) {
if (t.thID == 0)
continue;
while (!t.done) {
dispatchAll();
}
t.thread->join();
@@ -308,7 +317,7 @@ void IOP_Kernel::shutdown() {
}
IOP_Kernel::~IOP_Kernel() {
if(iso_disc_file) {
if (iso_disc_file) {
fclose(iso_disc_file);
}
}
+30 -30
View File
@@ -12,7 +12,7 @@
class IOP_Kernel;
namespace iop {
struct sceSifQueueData;
struct sceSifQueueData;
}
struct SifRpcCommand {
@@ -28,7 +28,6 @@ struct SifRpcCommand {
int copy_back_size;
};
struct SifRecord {
iop::sceSifQueueData* qd;
SifRpcCommand cmd;
@@ -36,14 +35,14 @@ struct SifRecord {
};
struct IopThreadRecord {
IopThreadRecord(std::string n, u32 (*f)(), s32 ID, IOP_Kernel* k) : name(n), function(f), thID(ID), kernel(k) {
IopThreadRecord(std::string n, u32 (*f)(), s32 ID, IOP_Kernel* k)
: name(n), function(f), thID(ID), kernel(k) {
kernelToThreadCV = new std::condition_variable;
threadToKernelCV = new std::condition_variable;
kernelToThreadMutex = new std::mutex;
threadToKernelMutex = new std::mutex;
}
~IopThreadRecord() {
delete kernelToThreadCV;
delete threadToKernelCV;
@@ -72,9 +71,8 @@ struct IopThreadRecord {
void dispatch();
};
class IOP_Kernel {
public:
public:
IOP_Kernel() {
// this ugly hack
threads.reserve(16);
@@ -90,7 +88,7 @@ public:
void SleepThread();
void WakeupThread(s32 id);
void dispatchAll();
void set_rpc_queue(iop::sceSifQueueData *qd, u32 thread);
void set_rpc_queue(iop::sceSifQueueData* qd, u32 thread);
void rpc_loop(iop::sceSifQueueData* qd);
void shutdown();
@@ -98,17 +96,15 @@ public:
* Resume the kernel.
*/
void returnToKernel() {
if(_currentThread < 0) throw std::runtime_error("tried to return to kernel not in a thread");
if (_currentThread < 0)
throw std::runtime_error("tried to return to kernel not in a thread");
threads[_currentThread].returnToKernel();
}
/*!
* Get current thread ID.
*/
s32 getCurrentThread() {
return _currentThread;
}
s32 getCurrentThread() { return _currentThread; }
/*!
* Create a message box
@@ -124,17 +120,18 @@ public:
* Returns if it got something.
*/
s32 PollMbx(void** msg, s32 mbx) {
if(_currentThread != -1 && threads.at(_currentThread).wantExit) {
if (_currentThread != -1 && threads.at(_currentThread).wantExit) {
// total hack - returning this value causes the ISO thread to error out and quit.
return -0x1a9;
}
// printf("poll %d %ld\n", mbx, mbxs.size());
if(mbx >= (s32) mbxs.size()) throw std::runtime_error("invalid PollMbx");
s32 gotSomething = mbxs[mbx].empty() ? 0 : 1;
if(gotSomething) {
// printf("poll %d %ld\n", mbx, mbxs.size());
if (mbx >= (s32)mbxs.size())
throw std::runtime_error("invalid PollMbx");
s32 gotSomething = mbxs[mbx].empty() ? 0 : 1;
if (gotSomething) {
void* thing = mbxs[mbx].front();
// printf("pop from msgbox %d %p\n", mbx, thing);
if(msg)
// printf("pop from msgbox %d %p\n", mbx, thing);
if (msg)
*msg = thing;
mbxs[mbx].pop();
}
@@ -146,24 +143,28 @@ public:
* Push something into a mbx
*/
s32 SendMbx(s32 mbx, void* value) {
if(mbx >= (s32) mbxs.size()) throw std::runtime_error("invalid SendMbx");
if (mbx >= (s32)mbxs.size())
throw std::runtime_error("invalid SendMbx");
mbxs[mbx].push(value);
// printf("push into messagebox %d %p\n", mbx, value);
// printf("mbx size %ld\n", mbxs.size());
// printf("push into messagebox %d %p\n", mbx, value);
// printf("mbx size %ld\n", mbxs.size());
return 0;
}
s32 CreateSema() {
return 1;
}
s32 CreateSema() { return 1; }
void read_disc_sectors(u32 sector, u32 sectors, void* buffer);
bool sif_busy(u32 id);
void sif_rpc(s32 rpcChannel, u32 fno, bool async, void *sendBuff, s32 sendSize, void *recvBuff, s32 recvSize);
void sif_rpc(s32 rpcChannel,
u32 fno,
bool async,
void* sendBuff,
s32 sendSize,
void* recvBuff,
s32 recvSize);
private:
private:
void setupThread(s32 id);
void runThread(s32 id);
s32 _nextThID = 0;
@@ -176,5 +177,4 @@ private:
std::mutex sif_mtx;
};
#endif //JAK_IOP_KERNEL_H
#endif // JAK_IOP_KERNEL_H
+11 -12
View File
@@ -13,7 +13,6 @@
#include <condition_variable>
#include "Timer.h"
constexpr int MAX_SYSTEM_THREADS = 16;
class SystemThreadInterface;
@@ -27,13 +26,13 @@ class SystemThreadManager;
* statistics.
*/
class SystemThread {
public:
public:
void start(std::function<void(SystemThreadInterface&)> f);
void join();
void stop();
SystemThread() = default;
private:
private:
friend class SystemThreadInterface;
friend class SystemThreadManager;
friend void* bootstrap_thread_func(void* thd);
@@ -41,7 +40,7 @@ private:
std::string name = "invalid";
std::thread thread;
SystemThreadManager* manager;
std::function<void(SystemThreadInterface &)> function;
std::function<void(SystemThreadInterface&)> function;
bool initialization_complete = false;
std::mutex initialization_mutex;
std::condition_variable initialization_cv;
@@ -59,15 +58,14 @@ private:
* The interfaces used by a thread in the runtime.
*/
class SystemThreadInterface {
public:
SystemThreadInterface(SystemThread* p) : thread(*p) {
}
public:
SystemThreadInterface(SystemThread* p) : thread(*p) {}
void initialization_complete();
void report_perf_stats();
bool get_want_exit() const;
void trigger_shutdown();
private:
private:
SystemThread& thread;
};
@@ -75,14 +73,15 @@ private:
* A manager of all threads in the runtime.
*/
class SystemThreadManager {
public:
public:
SystemThread& create_thread(const std::string& name);
void print_stats();
void shutdown();
void join();
private:
private:
std::array<SystemThread, MAX_SYSTEM_THREADS> threads;
int thread_count = 0;
};
#endif //RUNTIME_SYSTEMTHREAD_H
#endif // RUNTIME_SYSTEMTHREAD_H
+9 -16
View File
@@ -6,30 +6,23 @@
#include <cstdint>
class Timer {
public:
explicit Timer() {
start();
}
public:
explicit Timer() { start(); }
void start() {
}
void start() { clock_gettime(CLOCK_MONOTONIC, &_startTime); }
double getMs() {
return (double)getNs() / 1.e6;
}
double getMs() { return (double)getNs() / 1.e6; }
int64_t getNs() {
struct timespec now;
return (int64_t)(now.tv_nsec - _startTime.tv_nsec) + 1000000000 * (now.tv_sec - _startTime.tv_sec);
clock_gettime(CLOCK_MONOTONIC, &now);
return (int64_t)(now.tv_nsec - _startTime.tv_nsec) +
1000000000 * (now.tv_sec - _startTime.tv_sec);
}
double getSeconds() {
return (double)getNs() / 1.e9;
}
double getSeconds() { return (double)getNs() / 1.e9; }
struct timespec _startTime;
};
#endif //RUNTIME_TIMER_H
#endif // RUNTIME_TIMER_H
+1 -1
View File
@@ -5,7 +5,7 @@
struct Deci2Driver {
u16 protocol = 0;
void* opt = nullptr;
void (*handler)(s32 event, s32 param, void *opt) = nullptr;
void (*handler)(s32 event, s32 param, void* opt) = nullptr;
u8 id = 0;
bool active = false;
void* recv_buffer = nullptr;
+17 -17
View File
@@ -23,10 +23,7 @@
//#include "overlord/ssound.h"
//#include "overlord/stream.h"
IOP::IOP() {
}
IOP::IOP() {}
void IOP::send_status(IOP_Status new_status) {
{
@@ -38,16 +35,18 @@ void IOP::send_status(IOP_Status new_status) {
void IOP::wait_for_overlord_start_cmd() {
std::unique_lock<std::mutex> lk(iop_mutex);
if(status != IOP_WAIT_FOR_LOAD) return;
if (status != IOP_WAIT_FOR_LOAD)
return;
cv.wait(lk, [&]{return status != IOP_WAIT_FOR_LOAD;});
cv.wait(lk, [&] { return status != IOP_WAIT_FOR_LOAD; });
}
void IOP::wait_for_overlord_init_finish() {
std::unique_lock<std::mutex> lk(iop_mutex);
if(overlord_init_done) return;
if (overlord_init_done)
return;
cv.wait(lk, [&]{return overlord_init_done;});
cv.wait(lk, [&] { return overlord_init_done; });
}
void IOP::signal_overlord_init_finish() {
@@ -57,7 +56,7 @@ void IOP::signal_overlord_init_finish() {
}
void IOP::reset_allocator() {
for(auto x : allocations) {
for (auto x : allocations) {
free(x);
}
allocations.clear();
@@ -71,12 +70,12 @@ void* IOP::iop_alloc(int size) {
void IOP::wait_run_iop() {
std::unique_lock<std::mutex> lk(iters_mutex);
if(iop_iters_des > iop_iters_act) {
if (iop_iters_des > iop_iters_act) {
iop_iters_act++;
return;
}
iop_run_cv.wait(lk, [&]{return iop_iters_des > iop_iters_act;});
iop_run_cv.wait(lk, [&] { return iop_iters_des > iop_iters_act; });
iop_iters_act++;
}
@@ -87,7 +86,7 @@ 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
iop_iters_des += 100; // todo, tune this
iop_run_cv.notify_all();
}
@@ -95,7 +94,7 @@ IOP::~IOP() {
reset_allocator();
}
//void launch_iop(SystemThreadInterface& interfaces) {
// void launch_iop(SystemThreadInterface& interface) {
// IOP iop;
//
// printf("\n\n\n[IOP] Restart!\n");
@@ -140,14 +139,15 @@ IOP::~IOP() {
// iop.signal_overlord_init_finish();
//
// // IOP Kernel loop
// while(!interfaces.get_want_exit() && !iop.want_exit) {
// // the IOP kernel just runs at full blast, so we only run the IOP when the EE is waiting on the IOP.
// while(!interface.get_want_exit() && !iop.want_exit) {
// // the IOP kernel just runs at full blast, so we only run the IOP when the EE is waiting on
// the IOP.
// // Each time the EE is waiting on the IOP, it will run an iteration of the IOP kernel.
// iop.wait_run_iop();
// iop.kernel.dispatchAll();
// }
//
// // stop all threads in the iop kernel.
// // if the threads are not stopped nicely, we will deadlock on trying to destroy the kernel's condition variables.
// iop.kernel.shutdown();
// // if the threads are not stopped nicely, we will deadlock on trying to destroy the kernel's
// condition variables. iop.kernel.shutdown();
//}
+3 -9
View File
@@ -4,12 +4,7 @@
#include "common/common_types.h"
#include "IOP_Kernel.h"
enum IOP_Status {
IOP_WAIT_FOR_LOAD,
IOP_OVERLORD_INIT,
IOP_OVERLORD_RUN,
IOP_OVERLORD_STOP
};
enum IOP_Status { IOP_WAIT_FOR_LOAD, IOP_OVERLORD_INIT, IOP_OVERLORD_RUN, IOP_OVERLORD_STOP };
class IOP {
public:
@@ -25,9 +20,7 @@ class IOP {
void wait_run_iop();
void kill_from_ee();
void set_ee_main_mem(u8* mem) {
ee_main_mem = mem;
}
void set_ee_main_mem(u8* mem) { ee_main_mem = mem; }
IOP_Status status = IOP_WAIT_FOR_LOAD;
@@ -40,6 +33,7 @@ class IOP {
u64 iop_iters_des = 0;
u64 iop_iters_act = 0;
bool want_exit = false;
private:
std::vector<void*> allocations;
std::condition_variable cv;
+3 -2
View File
@@ -8,10 +8,11 @@ ELSE()
ENDIF()
add_subdirectory(emitter)
add_executable(goalc main.cpp)
add_executable(goalc main.cpp
compiler/Compiler.cpp)
IF (WIN32)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
ENDIF()
target_link_libraries(goalc util goos)
target_link_libraries(goalc util goos type_system)
+1
View File
@@ -0,0 +1 @@
#include "Compiler.h"
+12
View File
@@ -0,0 +1,12 @@
#ifndef JAK_COMPILER_H
#define JAK_COMPILER_H
#include "common/type_system/TypeSystem.h"
class Compiler {
public:
private:
TypeSystem m_ts;
};
#endif // JAK_COMPILER_H
+2 -2
View File
@@ -1,3 +1,3 @@
add_library(emitter
CodeTester.cpp
registers.cpp)
Register.cpp
CodeTester.cpp)
+81 -16
View File
@@ -1,65 +1,131 @@
/*!
* @file CodeTester.cpp
* The CodeTester is a utility to run the output of the compiler as part of a unit test.
* This is effective for tests which try all combinations of registers, etc.
*
* The CodeTester can't be used for tests requiring the full GOAL language/linking.
*/
#include <third-party/mman/mman.h>
#include <cstdio>
#include <sys/mman.h>
#include "CodeTester.h"
#include "Instruction.h"
#include "IGen.h"
namespace goal {
namespace emitter {
std::string CodeTester::dump_to_hex_string() {
CodeTester::CodeTester() : m_info(RegisterInfo::make_register_info()) {}
/*!
* Convert to a string for comparison against an assembler or tests.
*/
std::string CodeTester::dump_to_hex_string(bool nospace) {
std::string result;
char buff[32];
for (int i = 0; i < code_buffer_size; i++) {
sprintf(buff, "%02x ", code_buffer[i]);
if (nospace) {
sprintf(buff, "%02X", code_buffer[i]);
} else {
sprintf(buff, "%02x ", code_buffer[i]);
}
result += buff;
}
// remove trailing space
if (!result.empty()) {
if (!nospace && !result.empty()) {
result.pop_back();
}
return result;
}
/*!
* Add an instruction to the buffer.
*/
void CodeTester::emit(const Instruction& instr) {
code_buffer_size += instr.emit(code_buffer + code_buffer_size);
assert(code_buffer_size <= code_buffer_capacity);
}
void CodeTester::emit_set_gpr_as_return(X86R gpr) {
assert(is_gpr(gpr));
emit(IGen::mov_gpr64_gpr64(RAX, gpr));
}
/*!
* Add a return instruction to the buffer.
*/
void CodeTester::emit_return() {
emit(IGen::ret());
}
/*!
* Pop all GPRs off of the stack. Optionally exclude rax.
* Pops RSP always, which is weird, but doesn't cause issues.
*/
void CodeTester::emit_pop_all_gprs(bool exclude_rax) {
for(int i = 16; i-- > 0;) {
if(i != RAX || !exclude_rax) {
for (int i = 16; i-- > 0;) {
if (i != RAX || !exclude_rax) {
emit(IGen::pop_gpr64(i));
}
}
}
/*!
* Push all GPRs onto the stack. Optionally exclude RAX.
* Pushes RSP always, which is weird, but doesn't cause issues.
*/
void CodeTester::emit_push_all_gprs(bool exclude_rax) {
for (int i = 0; i < 16; i++) {
if(i != RAX || !exclude_rax) {
if (i != RAX || !exclude_rax) {
emit(IGen::push_gpr64(i));
}
}
}
/*!
* Push all xmm registers (all 128-bits) to the stack.
*/
void CodeTester::emit_push_all_xmms() {
emit(IGen::sub_gpr64_imm8s(RSP, 8));
for (int i = 0; i < 16; i++) {
emit(IGen::sub_gpr64_imm8s(RSP, 16));
emit(IGen::store128_gpr64_xmm128(RSP, XMM0 + i));
}
}
/*!
* Pop all xmm registers (all 128-bits) from the stack
*/
void CodeTester::emit_pop_all_xmms() {
for (int i = 0; i < 16; i++) {
emit(IGen::load128_xmm128_gpr64(XMM0 + i, RSP));
emit(IGen::add_gpr64_imm8s(RSP, 16));
}
emit(IGen::add_gpr64_imm8s(RSP, 8));
}
/*!
* Remove everything from the code buffer
*/
void CodeTester::clear() {
code_buffer_size = 0;
}
/*!
* Execute the buffered code with no arguments, return the value of RAX.
*/
u64 CodeTester::execute() {
return ((u64(*)())code_buffer)();
}
/*!
* Execute code buffer with arguments. Use get_c_abi_arg to figure out which registers the
* arguments will appear in (will handle windows/linux differences)
*/
u64 CodeTester::execute(u64 in0, u64 in1, u64 in2, u64 in3) {
return ((u64(*)(u64, u64, u64, u64))code_buffer)(in0, in1, in2, in3);
}
/*!
* Allocate a code buffer of the given size.
*/
void CodeTester::init_code_buffer(int capacity) {
code_buffer = (u8*)mmap(nullptr, capacity, PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
@@ -77,5 +143,4 @@ CodeTester::~CodeTester() {
munmap(code_buffer, code_buffer_capacity);
}
}
} // namespace goal
} // namespace emitter
+108 -14
View File
@@ -1,37 +1,131 @@
/*!
* @file CodeTester
* CodeTester is a utility which allows small segments of x86 code to be run, for the purpose of
* testing the compiler's code emitter. It is not suitable for testing compiled GOAL code.
* @file CodeTester.h
* The CodeTester is a utility to run the output of the compiler as part of a unit test.
* This is effective for tests which try all combinations of registers, etc.
*
* The CodeTester can't be used for tests requiring the full GOAL language/linking.
*/
#ifndef JAK1_CODETESTER_H
#define JAK1_CODETESTER_H
#ifndef JAK_CODETESTER_H
#define JAK_CODETESTER_H
#include <string>
#include "common/common_types.h"
#include "registers.h"
#include "Register.h"
#include "Instruction.h"
namespace goal {
namespace emitter {
class CodeTester {
public:
std::string dump_to_hex_string();
CodeTester();
std::string dump_to_hex_string(bool nospace = false);
void init_code_buffer(int capacity);
void emit_push_all_gprs(bool exclude_rax = false);
void emit_pop_all_gprs(bool exclude_rax = false);
void emit_push_all_xmms();
void emit_pop_all_xmms();
void emit_return();
void emit_set_gpr_as_return(X86R gpr);
void emit(const Instruction& instr);
u64 execute();
u64 execute(u64 in0, u64 in1, u64 in2, u64 in3);
/*!
* Execute the function, get the return value in RAX, convert to a T, and return it.
*/
template <typename T>
T execute_ret(u64 in0, u64 in1, u64 in2, u64 in3) {
u64 result_u64 = ((u64(*)(u64, u64, u64, u64))code_buffer)(in0, in1, in2, in3);
T result_T;
memcpy(&result_T, &result_u64, sizeof(T));
return result_T;
}
/*!
* Add data to the code buffer.
*/
template <typename T>
int emit_data(T x) {
auto ret = code_buffer_size;
assert(int(sizeof(T)) + code_buffer_size <= code_buffer_capacity);
memcpy(code_buffer + code_buffer_size, &x, sizeof(T));
code_buffer_size += sizeof(T);
return ret;
}
/*!
* Should allow emitter tests which run code to do the right thing on windows.
*/
Register get_c_abi_arg_reg(int i) {
#ifdef _WIN32
switch (i) {
case 0:
return RCX;
case 1:
return RDX;
case 2:
return R8;
case 3:
return R9;
default:
assert(false);
}
#else
switch (i) {
case 0:
return RDI;
case 1:
return RSI;
case 2:
return RDX;
case 3:
return RCX;
default:
assert(false);
}
#endif
}
/*!
* Get the name of the given register, for debugging.
*/
std::string reg_name(Register x) { return m_info.get_info(x).name; }
/*!
* Get number of bytes currently in use (offset of the next thing to be added)
*/
int size() const { return code_buffer_size; }
const u8* data() const { return code_buffer; }
/*!
* Write over existing data at the given offset.
*/
template <typename T>
void write(T x, int at) {
assert(at >= 0);
assert(int(sizeof(T)) + at <= code_buffer_capacity);
memcpy(code_buffer + at, &x, sizeof(T));
}
/*!
* Read existing data at the given offset.
*/
template <typename T>
T read(int at) {
assert(at >= 0);
assert(int(sizeof(T)) + at <= code_buffer_capacity);
T result;
memcpy(&result, code_buffer + at, sizeof(T));
return result;
}
void clear();
~CodeTester();
private:
int code_buffer_size = 0;
int code_buffer_capacity = 0;
u8* code_buffer = nullptr;
RegisterInfo m_info;
};
} // namespace goal
#endif // JAK1_CODETESTER_H
} // namespace emitter
#endif // JAK_CODETESTER_H
+1669 -670
View File
File diff suppressed because it is too large Load Diff
+319 -67
View File
@@ -1,15 +1,10 @@
/*!
* @file Instruction.h
* x86-64 instruction encoding
*/
#ifndef JAK1_INSTRUCTION_H
#define JAK1_INSTRUCTION_H
#ifndef JAK_INSTRUCTION_H
#define JAK_INSTRUCTION_H
#include <cassert>
#include "common/common_types.h"
namespace goal {
namespace emitter {
/*!
* The ModRM byte
*/
@@ -18,9 +13,7 @@ struct ModRM {
uint8_t reg_op;
uint8_t rm;
uint8_t operator()() const {
return (mod << 6) | (reg_op << 3) | (rm << 0);
}
uint8_t operator()() const { return (mod << 6) | (reg_op << 3) | (rm << 0); }
};
/*!
@@ -29,9 +22,7 @@ struct ModRM {
struct SIB {
uint8_t scale, index, base;
uint8_t operator()() const {
return (scale << 6) | (index << 3) | (base << 0);
}
uint8_t operator()() const { return (scale << 6) | (index << 3) | (base << 0); }
};
/*!
@@ -39,35 +30,33 @@ struct SIB {
*/
struct Imm {
Imm() = default;
Imm(uint8_t sz, uint64_t v) : size(sz), value(v) { }
Imm(uint8_t sz, uint64_t v) : size(sz), value(v) {}
uint8_t size;
union {
uint64_t value;
uint8_t v_arr[8];
};
};
/*!
* The REX prefix byte
*/
struct REX {
explicit REX(bool w = false, bool r = false, bool x = false, bool b = false) : W(w), R(r), X(x), B(b) { }
explicit REX(bool w = false, bool r = false, bool x = false, bool b = false)
: W(w), R(r), X(x), B(b) {}
// W - 64-bit operands
// R - reg extension
// X - SIB i extnsion
// B - other extension
bool W, R, X, B;
uint8_t operator()() const {
return (1 << 6) | (W << 3) | (R << 2) | (X << 1) | (B << 0);
}
uint8_t operator()() const { return (1 << 6) | (W << 3) | (R << 2) | (X << 1) | (B << 0); }
};
/*!
* A high-level description of an x86-64 opcode. It can emit itself.
*/
struct Instruction {
Instruction(uint8_t opcode) : op(opcode) { }
Instruction(uint8_t opcode) : op(opcode) {}
uint8_t op;
bool op2_set = false;
@@ -110,7 +99,8 @@ struct Instruction {
* Move opcode byte 0 to before the rex prefix.
*/
void swap_op0_rex() {
if(!set_rex) return;
if (!set_rex)
return;
auto temp = op;
op = m_rex;
m_rex = temp;
@@ -157,12 +147,12 @@ struct Instruction {
void set_modrm_and_rex(uint8_t reg, uint8_t rm, uint8_t mod, bool rex_w = false) {
bool rex_b = false, rex_r = false;
if(rm >= 8) {
if (rm >= 8) {
rm -= 8;
rex_b = true;
}
if(reg >= 8) {
if (reg >= 8) {
reg -= 8;
rex_r = true;
}
@@ -174,36 +164,218 @@ struct Instruction {
set(modrm);
if(rex_b || rex_w || rex_r) {
if (rex_b || rex_w || rex_r) {
set(REX(rex_w, rex_r, false, rex_b));
}
}
void set_modrm_and_rex_for_reg_plus_reg_plus_s8(uint8_t reg,
uint8_t addr1,
uint8_t addr2,
s8 offset,
bool rex_w) {
bool rex_b = false, rex_r = false, rex_x = false;
bool addr1_ext = false;
bool addr2_ext = false;
if (addr1 >= 8) {
addr1 -= 8;
addr1_ext = true;
}
if (addr2 >= 8) {
addr2 -= 8;
addr2_ext = true;
}
if (reg >= 8) {
reg -= 8;
rex_r = true;
}
ModRM modrm;
modrm.mod = 1; // no disp
modrm.rm = 4; // sib!
modrm.reg_op = reg;
SIB sib;
sib.scale = 0;
Imm imm2(1, offset);
// default addr1 in index
if (addr1 == 4) {
sib.index = addr2;
sib.base = addr1;
rex_x = addr2_ext;
rex_b = addr1_ext;
} else {
// addr1 in index
sib.index = addr1;
sib.base = addr2;
rex_x = addr1_ext;
rex_b = addr2_ext;
}
assert(sib.index != 4);
if (rex_b || rex_w || rex_r || rex_x) {
set(REX(rex_w, rex_r, rex_x, rex_b));
}
set(modrm);
set(sib);
set_disp(imm2);
}
void set_modrm_and_rex_for_reg_plus_reg_plus_s32(uint8_t reg,
uint8_t addr1,
uint8_t addr2,
s32 offset,
bool rex_w) {
bool rex_b = false, rex_r = false, rex_x = false;
bool addr1_ext = false;
bool addr2_ext = false;
if (addr1 >= 8) {
addr1 -= 8;
addr1_ext = true;
}
if (addr2 >= 8) {
addr2 -= 8;
addr2_ext = true;
}
if (reg >= 8) {
reg -= 8;
rex_r = true;
}
ModRM modrm;
modrm.mod = 2; // no disp
modrm.rm = 4; // sib!
modrm.reg_op = reg;
SIB sib;
sib.scale = 0;
Imm imm2(4, offset);
// default addr1 in index
if (addr1 == 4) {
sib.index = addr2;
sib.base = addr1;
rex_x = addr2_ext;
rex_b = addr1_ext;
} else {
// addr1 in index
sib.index = addr1;
sib.base = addr2;
rex_x = addr1_ext;
rex_b = addr2_ext;
}
assert(sib.index != 4);
if (rex_b || rex_w || rex_r || rex_x) {
set(REX(rex_w, rex_r, rex_x, rex_b));
}
set(modrm);
set(sib);
set_disp(imm2);
}
void set_modrm_and_rex_for_reg_plus_reg_addr(uint8_t reg,
uint8_t addr1,
uint8_t addr2,
bool rex_w = false,
bool rex_always = false) {
bool rex_b = false, rex_r = false, rex_x = false;
bool addr1_ext = false;
bool addr2_ext = false;
if (addr1 >= 8) {
addr1 -= 8;
addr1_ext = true;
}
if (addr2 >= 8) {
addr2 -= 8;
addr2_ext = true;
}
if (reg >= 8) {
reg -= 8;
rex_r = true;
}
ModRM modrm;
modrm.mod = 0; // no disp
modrm.rm = 4; // sib!
modrm.reg_op = reg;
SIB sib;
sib.scale = 0;
if (addr1 == 5 && addr2 == 5) {
sib.index = addr1;
sib.base = addr2;
rex_x = addr1_ext;
rex_b = addr2_ext;
modrm.mod = 1;
set_disp(Imm(1, 0));
} else {
// default addr1 in index
bool flipped = (addr1 == 4) || (addr2 == 5);
if (flipped) {
sib.index = addr2;
sib.base = addr1;
rex_x = addr2_ext;
rex_b = addr1_ext;
} else {
// addr1 in index
sib.index = addr1;
sib.base = addr2;
rex_x = addr1_ext;
rex_b = addr2_ext;
}
assert(sib.base != 5);
assert(sib.index != 4);
}
if (rex_b || rex_w || rex_r || rex_x || rex_always) {
set(REX(rex_w, rex_r, rex_x, rex_b));
}
set(modrm);
set(sib);
}
/*!
* Set modrm and rex as needed for two regs for an addressing mode.
* Will set SIB if R12 or RSP indexing is used.
*/
void set_modrm_and_rex_for_addr(uint8_t reg, uint8_t rm, uint8_t mod, bool rex_w = false) {
void set_modrm_and_rex_for_reg_addr(uint8_t reg, uint8_t rm, bool rex_w = false) {
bool rex_b = false, rex_r = false;
if(rm >= 8) {
if (rm >= 8) {
rm -= 8;
rex_b = true;
}
if(reg >= 8) {
if (reg >= 8) {
reg -= 8;
rex_r = true;
}
ModRM modrm;
modrm.mod = mod;
modrm.mod = 0;
modrm.reg_op = reg;
modrm.rm = rm;
set(modrm);
if(rm == 4) {
if (rm == 4) {
SIB sib;
sib.scale = 0;
sib.base = 4;
@@ -212,13 +384,40 @@ struct Instruction {
set(sib);
}
if(rex_b || rex_w || rex_r) {
if (rm == 5) {
modrm.mod = 1; // 1 byte imm
set_disp(Imm(1, 0));
}
set(modrm);
if (rex_b || rex_w || rex_r) {
set(REX(rex_w, rex_r, false, rex_b));
}
}
void set_modrm_and_rex_for_rip_plus_s32(uint8_t reg, s32 offset, bool rex_w = false) {
bool rex_r = false;
if (reg >= 8) {
reg -= 8;
rex_r = true;
}
ModRM modrm;
modrm.mod = 0;
modrm.reg_op = reg;
modrm.rm = 5; // use the RIP addressing mode
set(modrm);
if (rex_r || rex_w) {
set(REX(rex_w, rex_r, false, false));
}
set_disp(Imm(4, offset));
}
void add_rex() {
if(!set_rex) {
if (!set_rex) {
set(REX());
}
}
@@ -230,7 +429,7 @@ struct Instruction {
ModRM modrm;
bool rex_r = false;
if(reg >= 8) {
if (reg >= 8) {
reg -= 8;
rex_r = true;
}
@@ -238,13 +437,13 @@ struct Instruction {
modrm.mod = mod;
modrm.rm = 4; // use sib
modrm.rm = 4; // use sib
SIB sib;
sib.scale = 0;
sib.index = 4;
bool rex_b = false;
if(rm >= 8) {
if (rm >= 8) {
rex_b = true;
rm -= 8;
}
@@ -254,25 +453,30 @@ struct Instruction {
set(modrm);
set(sib);
if(rex_r || rex_w || rex_b) {
if (rex_r || rex_w || rex_b) {
set(REX(rex_w, rex_r, false, rex_b));
}
}
/*!
* Get the position of the disp immediate relative to the start of the instruction
*/
int offset_of_disp() const {
if(is_null) return 0;
if (is_null)
return 0;
assert(set_disp_imm);
int offset = 0;
if(set_rex) offset++;
offset++; // opcode
if(op2_set) offset++;
if(op3_set) offset++;
if(set_modrm) offset++;
if(set_sib) offset++;
if (set_rex)
offset++;
offset++; // opcode
if (op2_set)
offset++;
if (op3_set)
offset++;
if (set_modrm)
offset++;
if (set_sib)
offset++;
return offset;
}
@@ -280,16 +484,23 @@ struct Instruction {
* Get the position of the imm immediate relative to the start of the instruction
*/
int offset_of_imm() const {
if(is_null) return 0;
if (is_null)
return 0;
assert(set_imm);
int offset = 0;
if(set_rex) offset++;
offset++; // opcode
if(op2_set) offset++;
if(op3_set) offset++;
if(set_modrm) offset++;
if(set_sib) offset++;
if(set_disp_imm) offset += disp.size;
if (set_rex)
offset++;
offset++; // opcode
if (op2_set)
offset++;
if (op3_set)
offset++;
if (set_modrm)
offset++;
if (set_sib)
offset++;
if (set_disp_imm)
offset += disp.size;
return offset;
}
@@ -297,44 +508,85 @@ struct Instruction {
* Emit into a buffer and return how many bytes written (can be zero)
*/
uint8_t emit(uint8_t* buffer) const {
if(is_null) return 0;
if (is_null)
return 0;
uint8_t count = 0;
if(set_rex) {
if (set_rex) {
buffer[count++] = m_rex;
}
buffer[count++] = op;
if(op2_set) {
if (op2_set) {
buffer[count++] = op2;
}
if(op3_set) {
if (op3_set) {
buffer[count++] = op3;
}
if(set_modrm) {
if (set_modrm) {
buffer[count++] = m_modrm;
}
if(set_sib) {
if (set_sib) {
buffer[count++] = m_sib;
}
if(set_disp_imm) {
for(int i = 0; i < disp.size; i++) {
if (set_disp_imm) {
for (int i = 0; i < disp.size; i++) {
buffer[count++] = disp.v_arr[i];
}
}
if(set_imm) {
for(int i = 0; i < imm.size; i++) {
if (set_imm) {
for (int i = 0; i < imm.size; i++) {
buffer[count++] = imm.v_arr[i];
}
}
return count;
}
};
}
#endif // JAK1_INSTRUCTION_H
uint8_t length() const {
if (is_null)
return 0;
uint8_t count = 0;
if (set_rex) {
count++;
}
count++;
if (op2_set) {
count++;
}
if (op3_set) {
count++;
}
if (set_modrm) {
count++;
}
if (set_sib) {
count++;
}
if (set_disp_imm) {
for (int i = 0; i < disp.size; i++) {
count++;
}
}
if (set_imm) {
for (int i = 0; i < imm.size; i++) {
count++;
}
}
return count;
}
};
} // namespace emitter
#endif // JAK_INSTRUCTION_H
+33
View File
@@ -0,0 +1,33 @@
#include "Register.h"
namespace emitter {
RegisterInfo RegisterInfo::make_register_info() {
RegisterInfo info;
info.m_info[RAX] = {-1, false, false, "rax"};
info.m_info[RCX] = {3, false, false, "rcx"};
info.m_info[RDX] = {2, false, false, "rdx"};
info.m_info[RBX] = {-1, true, false, "rbx"};
info.m_info[RSP] = {-1, false, true, "rsp"};
info.m_info[RBP] = {-1, true, false, "rbp"};
info.m_info[RSI] = {1, false, false, "rsi"};
info.m_info[RDI] = {0, false, false, "rdi"};
info.m_info[R8] = {4, false, false, "r8"};
info.m_info[R9] = {5, false, false, "r9"};
info.m_info[R10] = {6, true, false, "r10"};
info.m_info[R11] = {7, true, false, "r11"};
info.m_info[R12] = {-1, true, false, "r12"};
info.m_info[R13] = {-1, false, true, "r13"}; // pp?
info.m_info[R14] = {-1, false, true, "r14"}; // st?
info.m_info[R15] = {-1, false, true, "r15"}; // offset.
info.m_arg_regs = std::array<Register, N_ARGS>({RDI, RSI, RDX, RCX, R8, R9, R10, R11});
info.m_saved_gprs = std::array<Register, N_SAVED_GPRS>({RBX, RBP, R10, R11, R12});
info.m_saved_xmms =
std::array<Register, N_SAVED_XMMS>({XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15});
return info;
}
} // namespace emitter
+134
View File
@@ -0,0 +1,134 @@
/*!
* @file Register.h
* Representation of an x86-64 Register.
*/
#ifndef JAK_REGISTER_H
#define JAK_REGISTER_H
#include <cassert>
#include <functional>
#include <array>
#include "common/common_types.h"
namespace emitter {
// registers by name
enum X86_REG : u8 {
RAX, // return, temp
RCX, // arg 3, temp
RDX, // arg 2, temp
RBX, // saved
RSP, // stack pointer (special)
RBP, // saved
RSI, // arg 1, temp
RDI, // arg 0, temp
R8, // arg 4, temp
R9, // arg 5, temp
R10, // arg 6, saved (arg in GOAL only)
R11, // arg 7, saved (arg in GOAL only)
R12, // saved
R13, // pp (special!)
R14, // st (special!)
R15, // offset (special!)
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
XMM8,
XMM9,
XMM10,
XMM11,
XMM12,
XMM13,
XMM14,
XMM15
};
class Register {
public:
Register() = default;
// intentionally not explicit so we can use X86_REGs in place of Registers
Register(int id) : m_id(id) {}
bool is_xmm() const { return m_id >= XMM0 && m_id <= XMM15; }
bool is_gpr() const { return m_id >= RAX && m_id <= R15; }
int hw_id() const {
if (is_xmm()) {
return m_id - XMM0;
} else if (is_gpr()) {
return m_id - RAX;
} else {
assert(false);
}
return 0xff;
}
int id() const { return m_id; }
struct hash {
auto operator()(const Register& x) const { return std::hash<u8>()(x.m_id); }
};
bool operator==(const Register& x) const { return m_id == x.m_id; }
bool operator!=(const Register& x) const { return m_id != x.m_id; }
private:
u8 m_id = 0xff;
};
class RegisterInfo {
public:
static constexpr int N_ARGS = 8;
static constexpr int N_REGS = 32;
static constexpr int N_SAVED_GPRS = 5;
static constexpr int N_SAVED_XMMS = 8;
static_assert(N_REGS - 1 == XMM15, "bad register count");
static RegisterInfo make_register_info();
struct Info {
int argument_id = -1; // -1 if not argument
bool saved = false; // does the callee save it?
bool special = false; // is it a special GOAL register?
std::string name;
};
const Info& get_info(Register r) const { return m_info.at(r.id()); }
Register get_arg_reg(int id) const { return m_arg_regs.at(id); }
Register get_saved_gpr(int id) const { return m_saved_gprs.at(id); }
Register get_saved_xmm(int id) const { return m_saved_xmms.at(id); }
Register get_process_reg() const { return R13; }
Register get_st_reg() const { return R14; }
Register get_offset_reg() const { return R15; }
Register get_ret_reg() const { return RAX; }
private:
RegisterInfo() = default;
std::array<Info, N_REGS> m_info;
std::array<Register, N_ARGS> m_arg_regs;
std::array<Register, N_SAVED_GPRS> m_saved_gprs;
std::array<Register, N_SAVED_XMMS> m_saved_xmms;
};
} // namespace emitter
#endif // JAK_REGISTER_H
-20
View File
@@ -1,20 +0,0 @@
#include "registers.h"
namespace goal {
bool is_gpr(u8 reg) {
return reg <= R15;
}
u8 get_nth_xmm(u8 id) {
return id + XMM0;
}
bool is_xmm(u8 reg) {
return reg >= XMM0 && reg <= XMM15;
}
u8 xmm_to_id(u8 reg) {
return reg - 16;
}
}
-111
View File
@@ -1,111 +0,0 @@
/*!
* @file registers.h
* Definitions and conventions for x86-64 registers.
*/
#ifndef JAK1_REGISTERS_H
#define JAK1_REGISTERS_H
#include "common/common_types.h"
namespace goal {
enum X86R : u8 {
RAX, // return, temp
RCX, // arg 3
RDX, // arg 2
RBX, // X saved
RSP, // stack pointer
RBP, // X base pointer (like fp)
RSI, // arg 1
RDI, // arg 0
R8, // arg 4
R9, // arg 5, saved
R10, // arg 6, saved (arg in GOAL only)
R11, // arg 7, saved (arg in GOAL only)
R12, // X saved - pp register (like s6)
R13, // X saved - function call register (like t9)
R14, // X saved - offset (added in GOAL x86)
R15, // X saved - st (like s7)
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
XMM8,
XMM9,
XMM10,
XMM11,
XMM12,
XMM13,
XMM14,
XMM15
};
// the argument registers of GOAL.
// We must have 8 to be compatible with GOAL's 8-argument function calls.
constexpr int ARG_REG_COUNT = 8;
// the first 6 are shared with Linux, and the last two are unique to GOAL.
constexpr X86R ARG_REGS[ARG_REG_COUNT] = {
X86R::RDI, X86R::RSI, X86R::RDX, X86R::RCX, X86R::R8, X86R::R9, X86R::R10, X86R::R11,
};
// The saved registers of GOAL. Note that RSP, RBP, R12, R13, R14, R15 shouldn't be changed by the
// caller, but these are special registers and won't be allocated to hold variables.
constexpr int SAVED_REG_COUNT = 4;
constexpr X86R SAVED_REGS[SAVED_REG_COUNT] = {X86R::RBX, X86R::R9, X86R::R10, X86R::R11};
// special registers
constexpr X86R PP_REG = X86R::R12;
constexpr X86R FUNC_REG = X86R::R13;
constexpr X86R OFF_REG = X86R::R14;
constexpr X86R ST_REG = X86R::R15;
constexpr X86R FP_REG = X86R::RBP;
constexpr X86R RET_REG = X86R::RAX;
// size in bytes of a pointer
constexpr int PTR_SIZE = 4;
// size in bytes of a general purpose register
constexpr int GPR_SIZE = 8;
constexpr const char* x86_gpr_names[] = {
"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10",
"r11", "r12", "r13", "r14", "r15", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5",
"xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"};
/*
Name Arg ID Clobber? Special
RAX - y return
RCX 3 y arg
RDX 2 y arg
RBX - n
RSP - n stack pointer
RBP - n base pointer
RSI 1 y arg
RDI 0 y arg
R8 4 y arg
R9 5 n arg
R10 6 n arg
R11 7 n arg
R12 - n pp
R13 - n func
R14 - n
R15
*/
bool is_gpr(u8 reg);
u8 get_nth_xmm(u8 id);
bool is_xmm(u8 reg);
u8 xmm_to_id(u8 reg);
} // namespace goal
#endif // JAK1_REGISTERS_H
+7
View File
@@ -85,6 +85,13 @@ Interpreter::Interpreter() {
load_goos_library();
}
Interpreter::~Interpreter() {
// There are some circular references that prevent shared_ptrs from cleaning up if we
// don't do this.
global_environment.as_env()->vars.clear();
goal_env.as_env()->vars.clear();
}
/*!
* Disable printfs on errors, to make test output look less messy.
*/
+1
View File
@@ -15,6 +15,7 @@ namespace goos {
class Interpreter {
public:
Interpreter();
~Interpreter();
void execute_repl();
void throw_eval_error(const Object& o, const std::string& err);
Object eval_with_rewind(const Object& obj, const std::shared_ptr<EnvironmentObject>& env);
+1 -1
View File
@@ -1,2 +1,2 @@
add_library(listener SHARED
Listener.cpp Deci2Server.cpp Deci2Server.h)
Listener.cpp)
-270
View File
@@ -1,270 +0,0 @@
/*!
* @file Deci2Server.cpp
* Basic implementation of a DECI2 server.
* Works with deci2.cpp (sceDeci2) to implement the networking on target
*/
#ifdef __unix__
#include <cstdio>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <cassert>
#include <utility>
#include "common/listener_common.h"
#include "common/versions.h"
#include "Deci2Server.h"
Deci2Server::Deci2Server(std::function<bool()> shutdown_callback) {
buffer = new char[BUFFER_SIZE];
want_exit = std::move(shutdown_callback);
}
Deci2Server::~Deci2Server() {
// if accept thread is running, kill it
if (accept_thread_running) {
kill_accept_thread = true;
accept_thread.join();
accept_thread_running = false;
}
delete[] buffer;
if (server_fd >= 0) {
close(server_fd);
}
if (new_sock >= 0) {
close(new_sock);
}
}
/*!
* Start waiting for the Listener to connect
bool Deci2Server::init() {
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
server_fd = -1;
return false;
}
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
printf("[Deci2Server] Failed to setsockopt 1\n");
close(server_fd);
server_fd = -1;
return false;
}
int one = 1;
if (setsockopt(server_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one))) {
printf("[Deci2Server] Failed to setsockopt 2\n");
close(server_fd);
server_fd = -1;
return false;
}
timeval timeout = {};
timeout.tv_sec = 0;
timeout.tv_usec = 100000;
if (setsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) {
printf("[Deci2Server] Failed to setsockopt 3\n");
close(server_fd);
server_fd = -1;
return false;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(DECI2_PORT);
if (bind(server_fd, (sockaddr*)&addr, sizeof(addr)) < 0) {
printf("[Deci2Server] Failed to bind\n");
close(server_fd);
server_fd = -1;
return false;
}
if (listen(server_fd, 0) < 0) {
printf("[Deci2Server] Failed to listen\n");
close(server_fd);
server_fd = -1;
return false;
}
server_initialized = true;
accept_thread_running = true;
kill_accept_thread = false;
accept_thread = std::thread(&Deci2Server::accept_thread_func, this);
return true;
}
/*!
* Return true if the listener is connected.
bool Deci2Server::check_for_listener() {
if (server_connected) {
if (accept_thread_running) {
accept_thread.join();
accept_thread_running = false;
}
return true;
} else {
return false;
}
}
/*!
* Send data from buffer. User must provide appropriate headers.
void Deci2Server::send_data(void* buf, u16 len) {
lock();
if (!server_connected) {
printf("[DECI2] send while not connected, not sending!\n");
} else {
uint16_t prog = 0;
while (prog < len) {
auto wrote = write(new_sock, (char*)(buf) + prog, len - prog);
prog += wrote;
if (!server_connected || want_exit()) {
unlock();
return;
}
}
}
unlock();
}
/*!
* Lock the DECI mutex. Should be done before modifying protocols.
void Deci2Server::lock() {
deci_mutex.lock();
}
/*!
* Unlock the DECI mutex. Should be done after modifying protocols.
void Deci2Server::unlock() {
deci_mutex.unlock();
}
/*!
* Wait for protocols to become ready.
* This avoids the case where we receive messages before protocol handlers are set up.
void Deci2Server::wait_for_protos_ready() {
if (protocols_ready)
return;
std::unique_lock<std::mutex> lk(deci_mutex);
cv.wait(lk, [&] { return protocols_ready; });
}
/*!
* Inform server that protocol handlers are ready.
* Will unblock wait_for_protos_ready and incoming messages will be dispatched to these
* protocols. You can change the protocol handlers, but you should lock the mutex before
* doing so.
void Deci2Server::send_proto_ready(Deci2Driver* drivers, int* driver_count) {
lock();
d2_drivers = drivers;
d2_driver_count = driver_count;
protocols_ready = true;
unlock();
cv.notify_all();
}
void Deci2Server::run() {
int desired_size = (int)sizeof(Deci2Header);
int got = 0;
while (got < desired_size) {
assert(got + desired_size < BUFFER_SIZE);
auto x = read(new_sock, buffer + got, desired_size - got);
if (want_exit()) {
return;
}
got += x > 0 ? x : 0;
}
auto* hdr = (Deci2Header*)(buffer);
printf("[DECI2] Got message:\n");
printf(" %d %d 0x%x %c -> %c\n", hdr->len, hdr->rsvd, hdr->proto, hdr->src, hdr->dst);
hdr->rsvd = got;
// see what protocol we got:
lock();
int handler = -1;
for (int i = 0; i < *d2_driver_count; i++) {
auto& prot = d2_drivers[i];
if (prot.active && prot.protocol) {
if (handler != -1) {
printf("[DECI2] Warning: more than on protocol handler for this message!\n");
}
handler = i;
}
}
if (handler == -1) {
printf("[DECI2] Warning: no handler for this message, ignoring...\n");
unlock();
return;
// throw std::runtime_error("no handler!");
}
auto& driver = d2_drivers[handler];
int sent_to_program = 0;
while (!want_exit() && (hdr->rsvd < hdr->len || sent_to_program < hdr->rsvd)) {
// send what we have to the program
if (sent_to_program < hdr->rsvd) {
// driver.next_recv_size = 0;
// driver.next_recv = nullptr;
driver.recv_buffer = buffer + sent_to_program;
driver.available_to_receive = hdr->rsvd - sent_to_program;
(driver.handler)(DECI2_READ, driver.available_to_receive, driver.opt);
// memcpy(driver.next_recv, buffer + sent_to_program, driver.next_recv_size);
sent_to_program += driver.recv_size;
}
// receive from network
if (hdr->rsvd < hdr->len) {
auto x = read(new_sock, buffer + hdr->rsvd, hdr->len - hdr->rsvd);
if (want_exit()) {
return;
}
got += x > 0 ? x : 0;
hdr->rsvd += got;
}
}
(driver.handler)(DECI2_READDONE, 0, driver.opt);
unlock();
}
/*!
* Background thread for waiting for the listener.
void Deci2Server::accept_thread_func() {
socklen_t l = sizeof(addr);
while (!kill_accept_thread) {
new_sock = accept(server_fd, (sockaddr*)&addr, &l);
if (new_sock >= 0) {
u32 versions[2] = {versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR};
send(new_sock, &versions, 8, 0); // todo, check result?
server_connected = true;
return;
}
}
}
#endif
-59
View File
@@ -1,59 +0,0 @@
/*!
* @file Deci2Server.h
* Basic implementation of a DECI2 server.
* Works with deci2.cpp (sceDeci2) to implement the networking on target
#ifndef JAK1_DECI2SERVER_H
#define JAK1_DECI2SERVER_H
#include <netinet/in.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include "game/system/deci_common.h" // todo, move me!
class Deci2Server {
public:
static constexpr int BUFFER_SIZE = 32 * 1024 * 1024;
Deci2Server(std::function<bool()> shutdown_callback);
~Deci2Server();
bool init();
bool check_for_listener();
void send_data(void* buf, u16 len);
void lock();
void unlock();
void wait_for_protos_ready();
void send_proto_ready(Deci2Driver* drivers, int* driver_count);
void run();
private:
void accept_thread_func();
bool kill_accept_thread = false;
char* buffer = nullptr;
int server_fd;
sockaddr_in addr;
int new_sock;
bool server_initialized = false;
bool accept_thread_running = false;
bool server_connected = false;
std::function<bool()> want_exit;
std::thread accept_thread;
std::condition_variable cv;
bool protocols_ready = false;
std::mutex deci_mutex;
Deci2Driver* d2_drivers = nullptr;
int* d2_driver_count = nullptr;
};
#endif // JAK1_DECI2SERVER_H
*/
+5 -7
View File
@@ -25,12 +25,11 @@ class Listener {
bool is_connected() const;
void disconnect();
private:
char* m_buffer = nullptr; //! buffer for incoming messages
bool m_connected = false; //! do we think we are connected?
bool receive_thread_running = false; //! is the receive thread unjoined?
int socket_fd = -1; //! socket
char* m_buffer = nullptr; //! buffer for incoming messages
bool m_connected = false; //! do we think we are connected?
bool receive_thread_running = false; //! is the receive thread unjoined?
int socket_fd = -1; //! socket
bool got_ack = false;
bool waiting_for_ack = false;
@@ -41,7 +40,6 @@ class Listener {
std::vector<std::string> message_record;
char ack_recv_buff[512];
};
}
} // namespace listener
#endif // JAK1_LISTENER_H
-1
View File
@@ -11,4 +11,3 @@ int main(int argc, char** argv) {
return 0;
}
+1 -1
View File
@@ -13,4 +13,4 @@ namespace util {
bool is_printable_char(char c) {
return c >= ' ' && c <= '~';
}
}
} // namespace util
+1 -1
View File
@@ -4,4 +4,4 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
export NEXT_DIR=$DIR
$DIR/build/test/goalc-test "$@"
$DIR/build/test/goalc-test --gtest_color=yes "$@"
+9 -5
View File
@@ -6,11 +6,15 @@ add_executable(goalc-test
test_reader.cpp
test_goos.cpp
test_listener_deci2.cpp
all_jak1_symbols.cpp
#test_kernel.cpp
#test_CodeTester.cpp
#test_type_system.cpp
)
test_kernel.cpp
all_jak1_symbols.cpp
test_type_system.cpp
test_CodeTester.cpp
test_emitter_slow.cpp
test_emitter_loads_and_store.cpp
test_emitter_xmm32.cpp
test_emitter_integer_math.cpp
)
IF (WIN32)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+102 -100
View File
@@ -2,13 +2,16 @@
* @file test_CodeTester.cpp
* Tests for the CodeTester, a tool for testing the emitter by emitting code and running it
* from within the test application.
*
* These tests should just make sure the basic functionality of CodeTester works, and that it
* can generate prologues/epilogues, and execute them without crashing.
*/
#include "gtest/gtest.h"
#include "goalc/emitter/CodeTester.h"
#include "goalc/emitter/IGen.h"
using namespace goal;
using namespace emitter;
TEST(CodeTester, prologue) {
CodeTester tester;
@@ -47,121 +50,120 @@ TEST(CodeTester, execute_push_pop_gprs) {
tester.execute();
}
TEST(CodeTester, load_constant_64_and_move_gpr_gpr_64) {
std::vector<u64> u64_constants = {0, UINT64_MAX, INT64_MAX, 7, 12};
// test we can load a 64-bit constant into all gprs, move it to any other gpr, and return it.
// rsp is skipping because that's the stack pointer and would prevent us from popping gprs after
TEST(CodeTester, xmm_store_128) {
CodeTester tester;
tester.init_code_buffer(256);
// movdqa [rbx], xmm3
// movdqa [r14], xmm3
// movdqa [rbx], xmm14
// movdqa [r14], xmm13
tester.emit(IGen::store128_gpr64_xmm128(RBX, XMM3));
tester.emit(IGen::store128_gpr64_xmm128(R14, XMM3));
tester.emit(IGen::store128_gpr64_xmm128(RBX, XMM14));
tester.emit(IGen::store128_gpr64_xmm128(R14, XMM13));
EXPECT_EQ(tester.dump_to_hex_string(),
"66 0f 7f 1b 66 41 0f 7f 1e 66 44 0f 7f 33 66 45 0f 7f 2e");
for (auto constant : u64_constants) {
for (int r1 = 0; r1 < 16; r1++) {
if (r1 == RSP) {
continue;
}
tester.clear();
tester.emit(IGen::store128_gpr64_xmm128(RSP, XMM1));
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 7f 0c 24"); // requires SIB byte.
for (int r2 = 0; r2 < 16; r2++) {
if (r2 == RSP) {
continue;
}
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(r1, constant));
tester.emit(IGen::mov_gpr64_gpr64(r2, r1));
tester.emit(IGen::mov_gpr64_gpr64(RAX, r2));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EQ(tester.execute(), constant);
}
}
}
tester.clear();
tester.emit(IGen::store128_gpr64_xmm128(R12, XMM13));
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 7f 2c 24"); // requires SIB byte and REX byte
tester.clear();
tester.emit(IGen::store128_gpr64_xmm128(RBP, XMM1));
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 7f 4d 00");
tester.clear();
tester.emit(IGen::store128_gpr64_xmm128(RBP, XMM11));
EXPECT_EQ(tester.dump_to_hex_string(), "66 44 0f 7f 5d 00");
tester.clear();
tester.emit(IGen::store128_gpr64_xmm128(R13, XMM2));
EXPECT_EQ(tester.dump_to_hex_string(), "66 41 0f 7f 55 00");
tester.clear();
tester.emit(IGen::store128_gpr64_xmm128(R13, XMM12));
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 7f 65 00");
}
TEST(CodeTester, load_constant_32_unsigned) {
std::vector<u64> u64_constants = {0, UINT32_MAX, INT32_MAX, 7, 12};
// test loading 32-bit constants, with all upper 32-bits zero.
// this uses a different opcode than 64-bit loads.
TEST(CodeTester, sub_gpr64_imm8) {
CodeTester tester;
tester.init_code_buffer(256);
for (auto constant : u64_constants) {
for (int r1 = 0; r1 < 16; r1++) {
if (r1 == RSP) {
continue;
}
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u32(r1, constant));
tester.emit(IGen::mov_gpr64_gpr64(RAX, r1));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EQ(tester.execute(), constant);
}
for (int i = 0; i < 16; i++) {
tester.emit(IGen::sub_gpr64_imm8s(i, -1));
}
EXPECT_EQ(tester.dump_to_hex_string(true),
"4883E8FF4883E9FF4883EAFF4883EBFF4883ECFF4883EDFF4883EEFF4883EFFF4983E8FF4983E9FF4983EA"
"FF4983EBFF4983ECFF4983EDFF4983EEFF4983EFFF");
}
TEST(CodeTester, load_constant_32_signed) {
std::vector<s32> s32_constants = {0, 1, INT32_MAX, INT32_MIN, 12, -1};
// test loading signed 32-bit constants. for values < 0 this will sign extend.
TEST(CodeTester, add_gpr64_imm8) {
CodeTester tester;
tester.init_code_buffer(256);
for (auto constant : s32_constants) {
for (int r1 = 0; r1 < 16; r1++) {
if (r1 == RSP) {
continue;
}
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_s32(r1, constant));
tester.emit(IGen::mov_gpr64_gpr64(RAX, r1));
tester.emit_pop_all_gprs(true);
tester.emit_return();
EXPECT_EQ(tester.execute(), constant);
}
for (int i = 0; i < 16; i++) {
tester.emit(IGen::add_gpr64_imm8s(i, -1));
}
EXPECT_EQ(tester.dump_to_hex_string(true),
"4883C0FF4883C1FF4883C2FF4883C3FF4883C4FF4883C5FF4883C6FF4883C7FF4983C0FF4983C1FF4983C2"
"FF4983C3FF4983C4FF4983C5FF4983C6FF4983C7FF");
}
TEST(CodeTester, xmm_move) {
std::vector<u32> u32_constants = {0, INT32_MAX, UINT32_MAX, 17};
// test moving between xmms (32-bit) and gprs.
TEST(CodeTester, xmm_load_128) {
CodeTester tester;
tester.init_code_buffer(256);
tester.emit(IGen::load128_xmm128_gpr64(XMM3, RBX));
tester.emit(IGen::load128_xmm128_gpr64(XMM3, R14));
tester.emit(IGen::load128_xmm128_gpr64(XMM14, RBX));
tester.emit(IGen::load128_xmm128_gpr64(XMM13, R14));
EXPECT_EQ(tester.dump_to_hex_string(),
"66 0f 6f 1b 66 41 0f 6f 1e 66 44 0f 6f 33 66 45 0f 6f 2e");
for(auto constant : u32_constants) {
for(int r1 = 0; r1 < 16; r1++) {
if(r1 == RSP) {
continue;
}
for(int r2 = 0; r2 < 16; r2++) {
if(r2 == RSP) {
continue;
}
for(int r3 = 0; r3 < 16; r3++) {
for(int r4 = 0; r4 < 16; r4++) {
tester.clear();
tester.emit_push_all_gprs(true);
// move constant to gpr
tester.emit(IGen::mov_gpr64_u32(r1, constant));
// move gpr to xmm
tester.emit(IGen::movd_xmm32_gpr32(get_nth_xmm(r3), r1));
// move xmm to xmm
tester.emit(IGen::mov_xmm32_xmm32(get_nth_xmm(r4), get_nth_xmm(r3)));
// move xmm to gpr
tester.emit(IGen::movd_gpr32_xmm32(r2, get_nth_xmm(r4)));
// return!
tester.emit(IGen::mov_gpr64_gpr64(RAX, r2));
tester.emit_return();
}
}
}
}
}
}
tester.clear();
tester.emit(IGen::load128_xmm128_gpr64(XMM1, RSP));
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 6f 0c 24"); // requires SIB byte.
tester.clear();
tester.emit(IGen::load128_xmm128_gpr64(XMM13, R12));
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 6f 2c 24"); // requires SIB byte and REX byte
tester.clear();
tester.emit(IGen::load128_xmm128_gpr64(XMM1, RBP));
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 6f 4d 00");
tester.clear();
tester.emit(IGen::load128_xmm128_gpr64(XMM11, RBP));
EXPECT_EQ(tester.dump_to_hex_string(), "66 44 0f 6f 5d 00");
tester.clear();
tester.emit(IGen::load128_xmm128_gpr64(XMM2, R13));
EXPECT_EQ(tester.dump_to_hex_string(), "66 41 0f 6f 55 00");
tester.clear();
tester.emit(IGen::load128_xmm128_gpr64(XMM12, R13));
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 6f 65 00");
}
TEST(CodeTester, push_pop_xmms) {
CodeTester tester;
tester.init_code_buffer(512);
tester.emit_push_all_xmms();
tester.emit_pop_all_xmms();
tester.emit_return();
tester.execute();
}
TEST(CodeTester, push_pop_all_the_things) {
CodeTester tester;
tester.init_code_buffer(512);
tester.emit_push_all_xmms();
tester.emit_push_all_gprs();
// ...
tester.emit_pop_all_gprs();
tester.emit_pop_all_xmms();
tester.emit_return();
tester.execute();
}
+628
View File
@@ -0,0 +1,628 @@
#include "third-party/fmt/core.h"
#include "gtest/gtest.h"
#include "goalc/emitter/CodeTester.h"
#include "goalc/emitter/IGen.h"
using namespace emitter;
TEST(EmitterIntegerMath, add_gpr64_imm8s) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
// test the ones that aren't rsp
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (auto val : vals) {
for (auto imm : imms) {
auto expected = val + imm;
tester.clear();
tester.emit_push_all_gprs(true);
// move initial value to register
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
// do the add
tester.emit(IGen::add_gpr64_imm8s(i, imm));
// move for return
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
tester.clear();
tester.emit(IGen::add_gpr64_imm8s(RSP, 12));
EXPECT_EQ(tester.dump_to_hex_string(), "48 83 c4 0c");
}
TEST(EmitterIntegerMath, add_gpr64_imm32s) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
// test the ones that aren't rsp
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (auto val : vals) {
for (auto imm : imms) {
auto expected = val + imm;
tester.clear();
tester.emit_push_all_gprs(true);
// move initial value to register
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
// do the add
tester.emit(IGen::add_gpr64_imm32s(i, imm));
// move for return
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
tester.clear();
tester.emit(IGen::add_gpr64_imm32s(RSP, 12));
EXPECT_EQ(tester.dump_to_hex_string(), "48 81 c4 0c 00 00 00");
}
TEST(EmitterIntegerMath, sub_gpr64_imm8s) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
// test the ones that aren't rsp
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (auto val : vals) {
for (auto imm : imms) {
auto expected = val - imm;
tester.clear();
tester.emit_push_all_gprs(true);
// move initial value to register
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
// do the add
tester.emit(IGen::sub_gpr64_imm8s(i, imm));
// move for return
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
tester.clear();
tester.emit(IGen::sub_gpr64_imm8s(RSP, 12));
EXPECT_EQ(tester.dump_to_hex_string(), "48 83 ec 0c");
}
TEST(EmitterIntegerMath, sub_gpr64_imm32s) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
// test the ones that aren't rsp
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (auto val : vals) {
for (auto imm : imms) {
auto expected = val - imm;
tester.clear();
tester.emit_push_all_gprs(true);
// move initial value to register
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
// do the add
tester.emit(IGen::sub_gpr64_imm32s(i, imm));
// move for return
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
tester.clear();
tester.emit(IGen::sub_gpr64_imm32s(RSP, 12));
EXPECT_EQ(tester.dump_to_hex_string(), "48 81 ec 0c 00 00 00");
}
TEST(EmitterIntegerMath, add_gpr64_gpr64) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (int j = 0; j < 16; j++) {
if (j == RSP || j == i) {
continue;
}
for (auto v1 : vals) {
for (auto v2 : vals) {
auto expected = v1 + v2;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v1));
tester.emit(IGen::mov_gpr64_u64(j, v2));
tester.emit(IGen::add_gpr64_gpr64(i, j));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterIntegerMath, sub_gpr64_gpr64) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (int j = 0; j < 16; j++) {
if (j == RSP || j == i) {
continue;
}
for (auto v1 : vals) {
for (auto v2 : vals) {
auto expected = v1 - v2;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v1));
tester.emit(IGen::mov_gpr64_u64(j, v2));
tester.emit(IGen::sub_gpr64_gpr64(i, j));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterIntegerMath, mul_gpr32_gpr32) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s32> vals = {
0, 1, -2, -20, 123123, INT32_MIN, INT32_MAX, INT32_MIN + 1, INT32_MAX - 1};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (int j = 0; j < 16; j++) {
if (j == RSP || j == i) {
continue;
}
for (auto v1 : vals) {
for (auto v2 : vals) {
// this is kind of weird behavior, but it's what the PS2 CPU does, I think.
// the lower 32-bits of the result are sign extended, even if this sign doesn't match
// the sign of the real product. This is true for both signed and unsigned multiply.
auto expected = ((s64(v1) * s64(v2)) << 32) >> 32;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, (s64)v1));
tester.emit(IGen::mov_gpr64_u64(j, (s64)v2));
tester.emit(IGen::imul_gpr32_gpr32(i, j));
tester.emit(IGen::movsx_r64_r32(RAX, i)); // weird PS2 sign extend.
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
if (result != expected) {
fmt::print("fail {} x {}: {}\n", v1, v2, tester.dump_to_hex_string());
}
}
}
}
}
}
TEST(EmitterIntegerMath, or_gpr64_gpr64) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (int j = 0; j < 16; j++) {
if (j == RSP || j == i) {
continue;
}
for (auto v1 : vals) {
for (auto v2 : vals) {
auto expected = v1 | v2;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v1));
tester.emit(IGen::mov_gpr64_u64(j, v2));
tester.emit(IGen::or_gpr64_gpr64(i, j));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterIntegerMath, and_gpr64_gpr64) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (int j = 0; j < 16; j++) {
if (j == RSP || j == i) {
continue;
}
for (auto v1 : vals) {
for (auto v2 : vals) {
auto expected = v1 & v2;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v1));
tester.emit(IGen::mov_gpr64_u64(j, v2));
tester.emit(IGen::and_gpr64_gpr64(i, j));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterIntegerMath, xor_gpr64_gpr64) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (int j = 0; j < 16; j++) {
if (j == RSP || j == i) {
continue;
}
for (auto v1 : vals) {
for (auto v2 : vals) {
auto expected = v1 ^ v2;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v1));
tester.emit(IGen::mov_gpr64_u64(j, v2));
tester.emit(IGen::xor_gpr64_gpr64(i, j));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterIntegerMath, not_gpr64) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (auto v1 : vals) {
auto expected = ~v1;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v1));
tester.emit(IGen::not_gpr64(i));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
TEST(EmitterIntegerMath, shl_gpr64_cl) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for (int i = 0; i < 16; i++) {
if (i == RSP || i == RCX) {
continue;
}
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v << sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v));
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
tester.emit(IGen::shl_gpr64_cl(i));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, shr_gpr64_cl) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
INT64_MAX, 117, 32, u64(-348473), 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for (int i = 0; i < 16; i++) {
if (i == RSP || i == RCX) {
continue;
}
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v >> sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v));
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
tester.emit(IGen::shr_gpr64_cl(i));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, sar_gpr64_cl) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for (int i = 0; i < 16; i++) {
if (i == RSP || i == RCX) {
continue;
}
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v >> sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v));
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
tester.emit(IGen::sar_gpr64_cl(i));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, shl_gpr64_u8) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v << sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v));
tester.emit(IGen::shl_gpr64_u8(i, sa));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, shr_gpr64_u8) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
INT64_MAX, 117, 32, u64(-348473), 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v >> sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v));
tester.emit(IGen::shr_gpr64_u8(i, sa));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, sar_gpr64_u8) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> sas = {0, 1, 23, 53, 64};
for (int i = 0; i < 16; i++) {
if (i == RSP) {
continue;
}
for (auto v : vals) {
for (auto sa : sas) {
auto expected = v >> sa;
tester.clear();
tester.emit_push_all_gprs(true);
tester.emit(IGen::mov_gpr64_u64(i, v));
tester.emit(IGen::sar_gpr64_u8(i, sa));
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
tester.emit_pop_all_gprs(true);
tester.emit_return();
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, jumps) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<int> reads;
auto x = IGen::jmp_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::je_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::jne_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::jle_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::jge_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::jl_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::jg_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::jbe_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::jae_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::jb_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
x = IGen::ja_32();
reads.push_back(tester.size() + x.offset_of_imm());
tester.emit(x);
for (auto off : reads) {
EXPECT_EQ(0, tester.read<s32>(off));
}
EXPECT_EQ(tester.dump_to_hex_string(true),
"E9000000000F84000000000F85000000000F8E000000000F8D000000000F8C000000000F8F000000000F86"
"000000000F83000000000F82000000000F8700000000");
}
TEST(EmitterIntegerMath, null) {
auto instr = IGen::null();
EXPECT_EQ(0, instr.emit(nullptr));
}

Some files were not shown because too many files have changed in this diff Show More