mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
[decompiler] bitfield support for sound-name
(#582)
* fix 64-bit fields in 128-bit bitfields * support sound-name * fix merge * support some more sound stuff in overlord
This commit is contained in:
parent
877e3d161c
commit
8faded6400
|
@ -68,7 +68,10 @@ Interpreter::Interpreter() {
|
||||||
{"type?", &Interpreter::eval_type},
|
{"type?", &Interpreter::eval_type},
|
||||||
{"current-method-type", &Interpreter::eval_current_method_type},
|
{"current-method-type", &Interpreter::eval_current_method_type},
|
||||||
{"fmt", &Interpreter::eval_format},
|
{"fmt", &Interpreter::eval_format},
|
||||||
{"error", &Interpreter::eval_error}};
|
{"error", &Interpreter::eval_error},
|
||||||
|
{"string-ref", &Interpreter::eval_string_ref},
|
||||||
|
{"string-length", &Interpreter::eval_string_length},
|
||||||
|
{"ash", &Interpreter::eval_ash}};
|
||||||
|
|
||||||
string_to_type = {{"empty-list", ObjectType::EMPTY_LIST},
|
string_to_type = {{"empty-list", ObjectType::EMPTY_LIST},
|
||||||
{"integer", ObjectType::INTEGER},
|
{"integer", ObjectType::INTEGER},
|
||||||
|
@ -1042,6 +1045,8 @@ IntType Interpreter::number_to_integer(const Object& obj) {
|
||||||
return obj.integer_obj.value;
|
return obj.integer_obj.value;
|
||||||
case ObjectType::FLOAT:
|
case ObjectType::FLOAT:
|
||||||
return (int64_t)obj.float_obj.value;
|
return (int64_t)obj.float_obj.value;
|
||||||
|
case ObjectType::CHAR:
|
||||||
|
return (int8_t)obj.char_obj.value;
|
||||||
default:
|
default:
|
||||||
throw_eval_error(obj, "object cannot be interpreted as a number!");
|
throw_eval_error(obj, "object cannot be interpreted as a number!");
|
||||||
}
|
}
|
||||||
|
@ -1527,4 +1532,44 @@ Object Interpreter::eval_error(const Object& form,
|
||||||
throw_eval_error(form, "Error: " + args.unnamed.at(0).as_string()->data);
|
throw_eval_error(form, "Error: " + args.unnamed.at(0).as_string()->data);
|
||||||
return EmptyListObject::make_new();
|
return EmptyListObject::make_new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object Interpreter::eval_string_ref(const Object& form,
|
||||||
|
Arguments& args,
|
||||||
|
const std::shared_ptr<EnvironmentObject>& env) {
|
||||||
|
(void)env;
|
||||||
|
vararg_check(form, args, {ObjectType::STRING, ObjectType::INTEGER}, {});
|
||||||
|
auto str = args.unnamed.at(0).as_string();
|
||||||
|
auto idx = args.unnamed.at(1).as_int();
|
||||||
|
if ((size_t)idx >= str->data.size()) {
|
||||||
|
throw_eval_error(form, fmt::format("String index {} out of range for string of size {}", idx,
|
||||||
|
str->data.size()));
|
||||||
|
}
|
||||||
|
return Object::make_char(str->data.at(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
Object Interpreter::eval_string_length(const Object& form,
|
||||||
|
Arguments& args,
|
||||||
|
const std::shared_ptr<EnvironmentObject>& env) {
|
||||||
|
(void)env;
|
||||||
|
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||||
|
auto str = args.unnamed.at(0).as_string();
|
||||||
|
return Object::make_integer(str->data.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
Object Interpreter::eval_ash(const Object& form,
|
||||||
|
Arguments& args,
|
||||||
|
const std::shared_ptr<EnvironmentObject>& env) {
|
||||||
|
(void)env;
|
||||||
|
vararg_check(form, args, {{}, {}}, {});
|
||||||
|
auto val = number_to_integer(args.unnamed.at(0));
|
||||||
|
auto sa = number_to_integer(args.unnamed.at(1));
|
||||||
|
if (sa >= 0 && sa < 64) {
|
||||||
|
return Object::make_integer(val << sa);
|
||||||
|
} else if (sa > -64) {
|
||||||
|
return Object::make_integer(val >> sa);
|
||||||
|
} else {
|
||||||
|
throw_eval_error(form, fmt::format("Shift amount {} is out of range", sa));
|
||||||
|
return EmptyListObject::make_new();
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace goos
|
} // namespace goos
|
||||||
|
|
|
@ -186,6 +186,15 @@ class Interpreter {
|
||||||
Object eval_error(const Object& form,
|
Object eval_error(const Object& form,
|
||||||
Arguments& args,
|
Arguments& args,
|
||||||
const std::shared_ptr<EnvironmentObject>& env);
|
const std::shared_ptr<EnvironmentObject>& env);
|
||||||
|
Object eval_string_ref(const Object& form,
|
||||||
|
Arguments& args,
|
||||||
|
const std::shared_ptr<EnvironmentObject>& env);
|
||||||
|
Object eval_string_length(const Object& form,
|
||||||
|
Arguments& args,
|
||||||
|
const std::shared_ptr<EnvironmentObject>& env);
|
||||||
|
Object eval_ash(const Object& form,
|
||||||
|
Arguments& args,
|
||||||
|
const std::shared_ptr<EnvironmentObject>& env);
|
||||||
|
|
||||||
// specials
|
// specials
|
||||||
Object eval_define(const Object& form,
|
Object eval_define(const Object& form,
|
||||||
|
|
|
@ -25,3 +25,7 @@ constexpr u32 TX_PAGE_VERSION = 7;
|
||||||
// GOAL kernel version (OpenGOAL changes this version from the game's version)
|
// GOAL kernel version (OpenGOAL changes this version from the game's version)
|
||||||
constexpr int KERNEL_VERSION_MAJOR = 2;
|
constexpr int KERNEL_VERSION_MAJOR = 2;
|
||||||
constexpr int KERNEL_VERSION_MINOR = 0;
|
constexpr int KERNEL_VERSION_MINOR = 0;
|
||||||
|
|
||||||
|
// OVERLORD version returned by an RPC
|
||||||
|
constexpr int IRX_VERSION_MAJOR = 2;
|
||||||
|
constexpr int IRX_VERSION_MINOR = 0;
|
|
@ -3091,7 +3091,7 @@ void push_asm_pcpyud_to_stack(const AsmOp* op,
|
||||||
if (bitfield_info && possible_r0->reg() == Register(Reg::GPR, Reg::R0)) {
|
if (bitfield_info && possible_r0->reg() == Register(Reg::GPR, Reg::R0)) {
|
||||||
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
|
auto base = pop_to_forms({*var}, env, pool, stack, true).at(0);
|
||||||
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
|
auto read_elt = pool.alloc_element<BitfieldAccessElement>(base, arg0_type);
|
||||||
read_elt->push_pcpyud();
|
read_elt->push_pcpyud(env.dts->ts, pool, env);
|
||||||
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, read_elt), true,
|
stack.push_value_to_reg(*dst, pool.alloc_single_form(nullptr, read_elt), true,
|
||||||
env.get_variable_type(*dst, true));
|
env.get_variable_type(*dst, true));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -144,6 +144,9 @@ BitfieldAccessElement::BitfieldAccessElement(Form* base_value, const TypeSpec& t
|
||||||
}
|
}
|
||||||
|
|
||||||
goos::Object BitfieldAccessElement::to_form_internal(const Env& env) const {
|
goos::Object BitfieldAccessElement::to_form_internal(const Env& env) const {
|
||||||
|
if (m_current_result) {
|
||||||
|
return m_current_result->to_form(env);
|
||||||
|
}
|
||||||
return pretty_print::build_list("incomplete-bitfield-access", m_base->to_form(env));
|
return pretty_print::build_list("incomplete-bitfield-access", m_base->to_form(env));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,11 +167,11 @@ void BitfieldAccessElement::get_modified_regs(RegSet& regs) const {
|
||||||
m_base->get_modified_regs(regs);
|
m_base->get_modified_regs(regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BitField& find_field(const TypeSystem& ts,
|
std::optional<BitField> try_find_field(const TypeSystem& ts,
|
||||||
const BitFieldType* type,
|
const BitFieldType* type,
|
||||||
int start_bit,
|
int start_bit,
|
||||||
int size,
|
int size,
|
||||||
std::optional<bool> looking_for_unsigned) {
|
std::optional<bool> looking_for_unsigned) {
|
||||||
for (auto& field : type->fields()) {
|
for (auto& field : type->fields()) {
|
||||||
if (field.size() == size && field.offset() == start_bit) {
|
if (field.size() == size && field.offset() == start_bit) {
|
||||||
// the GOAL compiler sign extended floats.
|
// the GOAL compiler sign extended floats.
|
||||||
|
@ -195,9 +198,22 @@ const BitField& find_field(const TypeSystem& ts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw std::runtime_error(
|
return {};
|
||||||
fmt::format("Unmatched field: start bit {}, size {}, signed? {}, type {}", start_bit, size,
|
}
|
||||||
!looking_for_unsigned, type->get_name()));
|
|
||||||
|
BitField find_field(const TypeSystem& ts,
|
||||||
|
const BitFieldType* type,
|
||||||
|
int start_bit,
|
||||||
|
int size,
|
||||||
|
std::optional<bool> looking_for_unsigned) {
|
||||||
|
auto result = try_find_field(ts, type, start_bit, size, looking_for_unsigned);
|
||||||
|
if (!result) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
fmt::format("Unmatched field: start bit {}, size {}, signed? {}, type {}", start_bit, size,
|
||||||
|
!looking_for_unsigned, type->get_name()));
|
||||||
|
} else {
|
||||||
|
return *result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -238,7 +254,7 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
||||||
int right = mr.maps.ints.at(2);
|
int right = mr.maps.ints.at(2);
|
||||||
int size = 64 - left;
|
int size = 64 - left;
|
||||||
int offset = left - right;
|
int offset = left - right;
|
||||||
auto& f = find_field(ts, type, offset, size, {});
|
auto f = find_field(ts, type, offset, size, {});
|
||||||
BitFieldDef def;
|
BitFieldDef def;
|
||||||
def.value = value;
|
def.value = value;
|
||||||
def.field_name = f.name();
|
def.field_name = f.name();
|
||||||
|
@ -261,7 +277,7 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
||||||
int right = *power_of_two;
|
int right = *power_of_two;
|
||||||
int size = 64 - left;
|
int size = 64 - left;
|
||||||
int offset = left - right;
|
int offset = left - right;
|
||||||
auto& f = find_field(ts, type, offset, size, {});
|
auto f = find_field(ts, type, offset, size, {});
|
||||||
BitFieldDef def;
|
BitFieldDef def;
|
||||||
def.value = value;
|
def.value = value;
|
||||||
def.field_name = f.name();
|
def.field_name = f.name();
|
||||||
|
@ -281,7 +297,7 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
||||||
int right = 0;
|
int right = 0;
|
||||||
int size = 64 - left;
|
int size = 64 - left;
|
||||||
int offset = left - right;
|
int offset = left - right;
|
||||||
auto& f = find_field(ts, type, offset, size, {});
|
auto f = find_field(ts, type, offset, size, {});
|
||||||
BitFieldDef def;
|
BitFieldDef def;
|
||||||
def.value = value;
|
def.value = value;
|
||||||
def.field_name = f.name();
|
def.field_name = f.name();
|
||||||
|
@ -296,7 +312,7 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
||||||
auto value = mr_sllv.maps.forms.at(0);
|
auto value = mr_sllv.maps.forms.at(0);
|
||||||
int size = 32;
|
int size = 32;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
auto& f = find_field(ts, type, offset, size, {});
|
auto f = find_field(ts, type, offset, size, {});
|
||||||
BitFieldDef def;
|
BitFieldDef def;
|
||||||
def.value = value;
|
def.value = value;
|
||||||
def.field_name = f.name();
|
def.field_name = f.name();
|
||||||
|
@ -309,11 +325,23 @@ std::optional<BitFieldDef> get_bitfield_initial_set(Form* form,
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void BitfieldAccessElement::push_pcpyud() {
|
void BitfieldAccessElement::push_pcpyud(const TypeSystem& ts, FormPool& pool, const Env& /*env*/) {
|
||||||
if (!m_steps.empty()) {
|
if (!m_steps.empty()) {
|
||||||
throw std::runtime_error("Unexpected pcpyud!");
|
throw std::runtime_error("Unexpected pcpyud!");
|
||||||
}
|
}
|
||||||
m_got_pcpyud = true;
|
m_got_pcpyud = true;
|
||||||
|
|
||||||
|
// look for a 64-bit field
|
||||||
|
auto type = ts.lookup_type(m_type);
|
||||||
|
auto as_bitfield = dynamic_cast<BitFieldType*>(type);
|
||||||
|
assert(as_bitfield);
|
||||||
|
auto field = try_find_field(ts, as_bitfield, 64, 64, true);
|
||||||
|
if (field) {
|
||||||
|
auto result =
|
||||||
|
pool.alloc_element<DerefElement>(m_base, false, DerefToken::make_field_name(field->name()));
|
||||||
|
result->inline_nested();
|
||||||
|
m_current_result = pool.alloc_single_form(this, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -571,6 +599,45 @@ BitFieldDef BitFieldDef::from_constant(const BitFieldConstantDef& constant, Form
|
||||||
return bfd;
|
return bfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Form* cast_sound_name(FormPool& pool, const Env& env, Form* in) {
|
||||||
|
auto matcher = Matcher::op(GenericOpMatcher::fixed(FixedOperatorKind::PCPYLD),
|
||||||
|
{Matcher::any(1), Matcher::any(0)});
|
||||||
|
auto mr = match(matcher, in);
|
||||||
|
if (!mr.matched) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hi = mr.maps.forms.at(1);
|
||||||
|
auto lo = mr.maps.forms.at(0);
|
||||||
|
|
||||||
|
auto hi_int = get_goal_integer_constant(hi, env);
|
||||||
|
auto lo_int = get_goal_integer_constant(lo, env);
|
||||||
|
if (!hi_int || !lo_int) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[17];
|
||||||
|
memcpy(name, &lo_int.value(), 8);
|
||||||
|
memcpy(name + 8, &hi_int.value(), 8);
|
||||||
|
name[16] = '\0';
|
||||||
|
|
||||||
|
bool got_zero = false;
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
if (name[i] == 0) {
|
||||||
|
got_zero = true;
|
||||||
|
} else {
|
||||||
|
if (got_zero) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool.alloc_single_element_form<ConstantTokenElement>(
|
||||||
|
nullptr, fmt::format("(static-sound-name \"{}\")", name));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Cast the given form to a bitfield.
|
* Cast the given form to a bitfield.
|
||||||
* If the form could have been a (new 'static 'bitfieldtype ...) it will attempt to generate this.
|
* If the form could have been a (new 'static 'bitfieldtype ...) it will attempt to generate this.
|
||||||
|
@ -581,6 +648,15 @@ Form* cast_to_bitfield(const BitFieldType* type_info,
|
||||||
const Env& env,
|
const Env& env,
|
||||||
Form* in) {
|
Form* in) {
|
||||||
in = strip_int_or_uint_cast(in);
|
in = strip_int_or_uint_cast(in);
|
||||||
|
|
||||||
|
if (type_info->get_name() == "sound-name") {
|
||||||
|
auto as_sound_name = cast_sound_name(pool, env, in);
|
||||||
|
if (as_sound_name) {
|
||||||
|
return as_sound_name;
|
||||||
|
}
|
||||||
|
// just do a normal cast if that failed.
|
||||||
|
return pool.alloc_single_element_form<CastElement>(nullptr, typespec, in);
|
||||||
|
}
|
||||||
// check if it's just a constant:
|
// check if it's just a constant:
|
||||||
auto in_as_atom = form_as_atom(in);
|
auto in_as_atom = form_as_atom(in);
|
||||||
if (in_as_atom && in_as_atom->is_int()) {
|
if (in_as_atom && in_as_atom->is_int()) {
|
||||||
|
|
|
@ -100,11 +100,15 @@ class BitfieldAccessElement : public FormElement {
|
||||||
const TypeSystem& ts,
|
const TypeSystem& ts,
|
||||||
FormPool& pool,
|
FormPool& pool,
|
||||||
const Env& env);
|
const Env& env);
|
||||||
void push_pcpyud();
|
void push_pcpyud(const TypeSystem& ts, FormPool& pool, const Env& env);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_got_pcpyud = false;
|
bool m_got_pcpyud = false;
|
||||||
Form* m_base = nullptr;
|
Form* m_base = nullptr;
|
||||||
|
|
||||||
|
// if we aren't sure if we are done or not, store the result here.
|
||||||
|
Form* m_current_result = nullptr;
|
||||||
|
|
||||||
TypeSpec m_type;
|
TypeSpec m_type;
|
||||||
std::vector<BitfieldManip> m_steps;
|
std::vector<BitfieldManip> m_steps;
|
||||||
};
|
};
|
||||||
|
@ -190,9 +194,9 @@ Form* cast_to_bitfield_enum(const EnumType* type_info, FormPool& pool, const Env
|
||||||
|
|
||||||
std::optional<u64> get_goal_integer_constant(Form* in, const Env&);
|
std::optional<u64> get_goal_integer_constant(Form* in, const Env&);
|
||||||
|
|
||||||
const BitField& find_field(const TypeSystem& ts,
|
BitField find_field(const TypeSystem& ts,
|
||||||
const BitFieldType* type,
|
const BitFieldType* type,
|
||||||
int start_bit,
|
int start_bit,
|
||||||
int size,
|
int size,
|
||||||
std::optional<bool> looking_for_unsigned);
|
std::optional<bool> looking_for_unsigned);
|
||||||
} // namespace decompiler
|
} // namespace decompiler
|
||||||
|
|
|
@ -161,3 +161,5 @@
|
||||||
- Fixed a bug where `''a` or similar repeated reader macros would generate a reader error.
|
- Fixed a bug where `''a` or similar repeated reader macros would generate a reader error.
|
||||||
- Fixed a bug where reader macros without a following form would trigger an assert.
|
- Fixed a bug where reader macros without a following form would trigger an assert.
|
||||||
- It is now possible to take the address of a lexical variable. The variable will be spilled to the stack automatically.
|
- It is now possible to take the address of a lexical variable. The variable will be spilled to the stack automatically.
|
||||||
|
- GOOS supports `string-ref`, `string-length`, `ash`, and characters can now be treated as a signed 8-bit number
|
||||||
|
- Fixed a bug where saved xmm registers might be clobbered when calling a C++ function that wasn't `format`.
|
|
@ -5,9 +5,6 @@
|
||||||
* Types used for the DGO Remote Procedure Call between the EE and the IOP
|
* Types used for the DGO Remote Procedure Call between the EE and the IOP
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef JAK1_DGO_RPC_TYPES_H
|
|
||||||
#define JAK1_DGO_RPC_TYPES_H
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
constexpr int DGO_RPC_ID = 0xdeb4;
|
constexpr int DGO_RPC_ID = 0xdeb4;
|
||||||
|
@ -29,5 +26,3 @@ struct RPC_Dgo_Cmd {
|
||||||
uint32_t buffer_heap_top;
|
uint32_t buffer_heap_top;
|
||||||
char name[16];
|
char name[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // JAK1_DGO_RPC_TYPES_H
|
|
||||||
|
|
|
@ -5,10 +5,5 @@
|
||||||
* Types used for the Loader Remote Procedure Call between the EE and the IOP
|
* Types used for the Loader Remote Procedure Call between the EE and the IOP
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef JAK1_LOADER_RPC_TYPES_H
|
|
||||||
#define JAK1_LOADER_RPC_TYPES_H
|
|
||||||
|
|
||||||
constexpr int LOADER_RPC_ID = 0xdeb2;
|
constexpr int LOADER_RPC_ID = 0xdeb2;
|
||||||
constexpr int LOADER_RPC_CHANNEL = 1;
|
constexpr int LOADER_RPC_CHANNEL = 1;
|
||||||
|
|
||||||
#endif // JAK1_LOADER_RPC_TYPES_H
|
|
||||||
|
|
|
@ -6,6 +6,44 @@
|
||||||
|
|
||||||
SECTION .text
|
SECTION .text
|
||||||
|
|
||||||
|
;; Call C++ code on linux, from GOAL, using Linux calling convention.
|
||||||
|
global _arg_call_linux
|
||||||
|
_arg_call_linux:
|
||||||
|
pop rax
|
||||||
|
push r10
|
||||||
|
push r11
|
||||||
|
sub rsp, 8
|
||||||
|
|
||||||
|
; xmm stuff
|
||||||
|
sub rsp, 128
|
||||||
|
movaps [rsp], xmm8
|
||||||
|
movaps [rsp + 16], xmm9
|
||||||
|
movaps [rsp + 32], xmm10
|
||||||
|
movaps [rsp + 48], xmm11
|
||||||
|
movaps [rsp + 64], xmm12
|
||||||
|
movaps [rsp + 80], xmm13
|
||||||
|
movaps [rsp + 96], xmm14
|
||||||
|
movaps [rsp + 112], xmm15
|
||||||
|
|
||||||
|
call rax
|
||||||
|
|
||||||
|
movaps xmm8, [rsp]
|
||||||
|
movaps xmm9, [rsp + 16]
|
||||||
|
movaps xmm10, [rsp + 32]
|
||||||
|
movaps xmm11, [rsp + 48]
|
||||||
|
movaps xmm12, [rsp + 64]
|
||||||
|
movaps xmm13, [rsp + 80]
|
||||||
|
movaps xmm14, [rsp + 96]
|
||||||
|
movaps xmm15, [rsp + 112]
|
||||||
|
add rsp, 128
|
||||||
|
|
||||||
|
add rsp, 8
|
||||||
|
pop r11
|
||||||
|
pop r10
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
;; Call C++ code on linux, from GOAL. Pug arguments on the stack and put a pointer to this array in the first arg.
|
||||||
;; this function pushes all 8 OpenGOAL registers into a stack array.
|
;; this function pushes all 8 OpenGOAL registers into a stack array.
|
||||||
;; then it calls the function pointed to by rax with a pointer to this array.
|
;; then it calls the function pointed to by rax with a pointer to this array.
|
||||||
;; it returns the return value of the called function.
|
;; it returns the return value of the called function.
|
||||||
|
@ -64,7 +102,7 @@ _stack_call_linux:
|
||||||
; return!
|
; return!
|
||||||
ret
|
ret
|
||||||
|
|
||||||
;; windows implementation of stack_call
|
;; Call C++ code on windows, from GOAL. Pug arguments on the stack and put a pointer to this array in the first arg.
|
||||||
global _stack_call_win32
|
global _stack_call_win32
|
||||||
_stack_call_win32:
|
_stack_call_win32:
|
||||||
pop rax
|
pop rax
|
||||||
|
@ -124,6 +162,7 @@ _stack_call_win32:
|
||||||
ret
|
ret
|
||||||
|
|
||||||
;; The _call_goal_asm function is used to call a GOAL function from C.
|
;; The _call_goal_asm function is used to call a GOAL function from C.
|
||||||
|
;; It calls on the parent stack, which is a bad idea if your stack is not already a GOAL stack.
|
||||||
;; It supports up to 3 arguments and a return value.
|
;; It supports up to 3 arguments and a return value.
|
||||||
;; This should be called with the arguments:
|
;; This should be called with the arguments:
|
||||||
;; - first goal arg
|
;; - first goal arg
|
||||||
|
@ -163,6 +202,7 @@ _call_goal_asm_linux:
|
||||||
pop r13
|
pop r13
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
;; Call goal, but switch stacks.
|
||||||
global _call_goal_on_stack_asm_linux
|
global _call_goal_on_stack_asm_linux
|
||||||
|
|
||||||
_call_goal_on_stack_asm_linux:
|
_call_goal_on_stack_asm_linux:
|
||||||
|
|
|
@ -318,39 +318,44 @@ u64 make_string_from_c(const char* c_str) {
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void _arg_call_linux();
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* This creates an OpenGOAL function from a C++ function. Only 6 arguments can be accepted.
|
* This creates an OpenGOAL function from a C++ function. Only 6 arguments can be accepted.
|
||||||
* But calling this function is very fast and doesn't use the stack.
|
* But calling this function is fast. It used to be really fast but wrong.
|
||||||
*/
|
*/
|
||||||
Ptr<Function> make_function_from_c_linux(void* func) {
|
Ptr<Function> make_function_from_c_linux(void* func) {
|
||||||
// allocate a function object on the global heap
|
|
||||||
auto mem = Ptr<u8>(
|
auto mem = Ptr<u8>(
|
||||||
alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP, *(s7 + FIX_SYM_FUNCTION_TYPE), 0x40));
|
alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP, *(s7 + FIX_SYM_FUNCTION_TYPE), 0x40));
|
||||||
auto f = (uint64_t)func;
|
auto f = (uint64_t)func;
|
||||||
auto fp = (u8*)&f;
|
auto target_function = (u8*)&f;
|
||||||
|
auto trampoline_function_addr = _arg_call_linux;
|
||||||
|
auto trampoline = (u8*)&trampoline_function_addr;
|
||||||
|
|
||||||
int i = 0;
|
// movabs rax, target_function
|
||||||
// we will put the function address in RAX with a movabs rax, imm8
|
int offset = 0;
|
||||||
mem.c()[i++] = 0x48;
|
mem.c()[offset++] = 0x48;
|
||||||
mem.c()[i++] = 0xb8;
|
mem.c()[offset++] = 0xb8;
|
||||||
for (int j = 0; j < 8; j++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
mem.c()[i++] = fp[j];
|
mem.c()[offset++] = target_function[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// push r10
|
// push rax
|
||||||
// push r11
|
mem.c()[offset++] = 0x50;
|
||||||
// sub rsp, 8
|
|
||||||
// call rax
|
// movabs rax, trampoline
|
||||||
// add rsp, 8
|
mem.c()[offset++] = 0x48;
|
||||||
// pop r11
|
mem.c()[offset++] = 0xb8;
|
||||||
// pop r10
|
for (int i = 0; i < 8; i++) {
|
||||||
// ret
|
mem.c()[offset++] = trampoline[i];
|
||||||
for (auto x : {0x41, 0x52, 0x41, 0x53, 0x48, 0x83, 0xEC, 0x08, 0xFF, 0xD0, 0x48, 0x83, 0xC4, 0x08,
|
|
||||||
0x41, 0x5B, 0x41, 0x5A, 0xC3}) {
|
|
||||||
mem.c()[i++] = x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the C function's ret will return to the caller of this trampoline.
|
// jmp rax
|
||||||
|
mem.c()[offset++] = 0xff;
|
||||||
|
mem.c()[offset++] = 0xe0;
|
||||||
|
// the asm function's ret will return to the caller of this (GOAL code) directlyz.
|
||||||
|
|
||||||
// CacheFlush(mem, 0x34);
|
// CacheFlush(mem, 0x34);
|
||||||
|
|
||||||
|
@ -403,8 +408,6 @@ ret
|
||||||
mem.c()[i++] = x;
|
mem.c()[i++] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the C function's ret will return to the caller of this trampoline.
|
|
||||||
|
|
||||||
// CacheFlush(mem, 0x34);
|
// CacheFlush(mem, 0x34);
|
||||||
|
|
||||||
return mem.cast<Function>();
|
return mem.cast<Function>();
|
||||||
|
|
|
@ -270,6 +270,8 @@ u32 ISOThread() {
|
||||||
InitDriver(temp_buffer->get_data()); // unblocks InitISOFS's WaitMbx
|
InitDriver(temp_buffer->get_data()); // unblocks InitISOFS's WaitMbx
|
||||||
FreeBuffer(temp_buffer);
|
FreeBuffer(temp_buffer);
|
||||||
|
|
||||||
|
VagCommand* in_progress_vag_command = nullptr;
|
||||||
|
|
||||||
// main CD/DVD read loop
|
// main CD/DVD read loop
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
|
@ -358,7 +360,27 @@ u32 ISOThread() {
|
||||||
load_single_cmd->callback_function = RunDGOStateMachine;
|
load_single_cmd->callback_function = RunDGOStateMachine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (msg_from_mbx->cmd_id == LOAD_SOUND_BANK) {
|
||||||
|
// if there's an in progress vag command, try again.
|
||||||
|
if (!in_progress_vag_command || !in_progress_vag_command->field_0x3c) {
|
||||||
|
auto buff = TryAllocateBuffer(BUFFER_PAGE_SIZE);
|
||||||
|
if (!buff) {
|
||||||
|
// no buffers, try again.
|
||||||
|
SendMbx(iso_mbx, msg_from_mbx);
|
||||||
|
} else {
|
||||||
|
// todo - actual load here
|
||||||
|
auto* cmd = (SoundBankLoadCommand*)msg_from_mbx;
|
||||||
|
printf("Ignoring request to load sound bank %s\n", cmd->bank_name);
|
||||||
|
FreeBuffer(buff);
|
||||||
|
ReturnMessage(msg_from_mbx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// just try again...
|
||||||
|
SendMbx(iso_mbx, msg_from_mbx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
printf("[OVERLORD] Unknown ISOThread message id 0x%x\n", msg_from_mbx->cmd_id);
|
printf("[OVERLORD] Unknown ISOThread message id 0x%x\n", msg_from_mbx->cmd_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
* This is a huge mess
|
* This is a huge mess
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef JAK_V2_ISO_H
|
|
||||||
#define JAK_V2_ISO_H
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "isocommon.h"
|
#include "isocommon.h"
|
||||||
|
|
||||||
|
@ -16,5 +13,3 @@ void iso_init_globals();
|
||||||
FileRecord* FindISOFile(const char* name);
|
FileRecord* FindISOFile(const char* name);
|
||||||
u32 GetISOFileLength(FileRecord* f);
|
u32 GetISOFileLength(FileRecord* f);
|
||||||
u32 InitISOFS(const char* fs_mode, const char* loading_screen);
|
u32 InitISOFS(const char* fs_mode, const char* loading_screen);
|
||||||
|
|
||||||
#endif // JAK_V2_ISO_H
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "iso_api.h"
|
#include "iso_api.h"
|
||||||
#include "game/sce/iop.h"
|
#include "game/sce/iop.h"
|
||||||
#include "common/log/log.h"
|
#include "common/log/log.h"
|
||||||
|
#include "common/util/assert.h"
|
||||||
|
#include "sbank.h"
|
||||||
|
|
||||||
using namespace iop;
|
using namespace iop;
|
||||||
|
|
||||||
|
@ -65,3 +67,20 @@ s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length,
|
||||||
}
|
}
|
||||||
return cmd.length_to_copy;
|
return cmd.length_to_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Send a command to the ISO thread to load a sound bank. This will sleep the calling thread
|
||||||
|
* until the load has completed.
|
||||||
|
*/
|
||||||
|
void LoadSoundBank(const char* bank_name, SoundBank* bank) {
|
||||||
|
(void)bank;
|
||||||
|
assert(strlen(bank_name) < 16);
|
||||||
|
SoundBankLoadCommand cmd;
|
||||||
|
cmd.cmd_id = LOAD_SOUND_BANK;
|
||||||
|
cmd.messagebox_to_reply = 0;
|
||||||
|
cmd.thread_id = GetThreadId();
|
||||||
|
strcpy(cmd.bank_name, bank_name);
|
||||||
|
cmd.bank = bank;
|
||||||
|
SendMbx(iso_mbx, &cmd);
|
||||||
|
SleepThread(); // wait for finish.
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
#include "isocommon.h"
|
#include "isocommon.h"
|
||||||
|
|
||||||
|
struct SoundBank;
|
||||||
|
|
||||||
s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length);
|
s32 LoadISOFileToIOP(FileRecord* file, void* addr, uint32_t length);
|
||||||
s32 LoadISOFileToEE(FileRecord* file, uint32_t ee_addr, uint32_t length);
|
s32 LoadISOFileToEE(FileRecord* file, uint32_t ee_addr, uint32_t length);
|
||||||
s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length, uint32_t offset);
|
s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length, uint32_t offset);
|
||||||
|
void LoadSoundBank(const char* bank_name, SoundBank* bank);
|
|
@ -35,7 +35,6 @@ VagCommand vag_cmds[N_VAG_CMDS];
|
||||||
|
|
||||||
static s32 sSema;
|
static s32 sSema;
|
||||||
|
|
||||||
IsoBufferHeader* TryAllocateBuffer(uint32_t size);
|
|
||||||
void ReleaseMessage(IsoMessage* cmd);
|
void ReleaseMessage(IsoMessage* cmd);
|
||||||
void FreeVAGCommand(VagCommand* cmd);
|
void FreeVAGCommand(VagCommand* cmd);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef JAK_V2_ISO_QUEUE_H
|
|
||||||
#define JAK_V2_ISO_QUEUE_H
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "isocommon.h"
|
#include "isocommon.h"
|
||||||
|
|
||||||
|
@ -15,5 +12,4 @@ void UnqueueMessage(IsoMessage* cmd);
|
||||||
IsoMessage* GetMessage();
|
IsoMessage* GetMessage();
|
||||||
void ProcessMessageData();
|
void ProcessMessageData();
|
||||||
void ReturnMessage(IsoMessage* cmd);
|
void ReturnMessage(IsoMessage* cmd);
|
||||||
|
IsoBufferHeader* TryAllocateBuffer(uint32_t size);
|
||||||
#endif // JAK_V2_ISO_QUEUE_H
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ constexpr int LOAD_TO_EE_CMD_ID = 0x100; // command to load file to ee
|
||||||
constexpr int LOAD_TO_IOP_CMD_ID = 0x101; // command to load to iop
|
constexpr int LOAD_TO_IOP_CMD_ID = 0x101; // command to load to iop
|
||||||
constexpr int LOAD_TO_EE_OFFSET_CMD_ID = 0x102; // command to load file to ee with offset.
|
constexpr int LOAD_TO_EE_OFFSET_CMD_ID = 0x102; // command to load file to ee with offset.
|
||||||
constexpr int LOAD_DGO_CMD_ID = 0x200; // command to load DGO
|
constexpr int LOAD_DGO_CMD_ID = 0x200; // command to load DGO
|
||||||
|
constexpr int LOAD_SOUND_BANK = 0x300; // Command to load a sound bank
|
||||||
|
|
||||||
constexpr int MAX_ISO_FILES = 350; // maximum files on FS
|
constexpr int MAX_ISO_FILES = 350; // maximum files on FS
|
||||||
constexpr int MAX_OPEN_FILES = 16; // maximum number of open files at a time.
|
constexpr int MAX_OPEN_FILES = 16; // maximum number of open files at a time.
|
||||||
|
@ -117,6 +118,13 @@ struct VagCommand : public IsoMessage {
|
||||||
// 0x6c max
|
// 0x6c max
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SoundBank;
|
||||||
|
|
||||||
|
struct SoundBankLoadCommand : public IsoMessage {
|
||||||
|
char bank_name[16];
|
||||||
|
SoundBank* bank;
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* DGO Load State Machine states.
|
* DGO Load State Machine states.
|
||||||
*/
|
*/
|
||||||
|
@ -167,16 +175,16 @@ struct PriStackEntry {
|
||||||
* API to access files. There are debug modes + reading from an ISO filesystem.
|
* API to access files. There are debug modes + reading from an ISO filesystem.
|
||||||
*/
|
*/
|
||||||
struct IsoFs {
|
struct IsoFs {
|
||||||
int (*init)(u8*);
|
int (*init)(u8*); // 0
|
||||||
FileRecord* (*find)(const char*);
|
FileRecord* (*find)(const char*); // 4
|
||||||
FileRecord* (*find_in)(const char*);
|
FileRecord* (*find_in)(const char*); // 8
|
||||||
uint32_t (*get_length)(FileRecord*);
|
uint32_t (*get_length)(FileRecord*); // c
|
||||||
LoadStackEntry* (*open)(FileRecord*, int32_t);
|
LoadStackEntry* (*open)(FileRecord*, int32_t); // 10
|
||||||
LoadStackEntry* (*open_wad)(FileRecord*, int32_t);
|
LoadStackEntry* (*open_wad)(FileRecord*, int32_t); // 14
|
||||||
void (*close)(LoadStackEntry*);
|
void (*close)(LoadStackEntry*); // 18
|
||||||
uint32_t (*begin_read)(LoadStackEntry*, void*, int32_t);
|
uint32_t (*begin_read)(LoadStackEntry*, void*, int32_t); // 1c
|
||||||
uint32_t (*sync_read)();
|
uint32_t (*sync_read)(); // 20
|
||||||
uint32_t (*load_sound_bank)(char*, void*);
|
uint32_t (*load_sound_bank)(char*, void*); // 24
|
||||||
uint32_t (*load_music)(char*, void*);
|
uint32_t (*load_music)(char*, void*);
|
||||||
void (*poll_drive)();
|
void (*poll_drive)();
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "iso.h"
|
#include "iso.h"
|
||||||
#include "ssound.h"
|
#include "ssound.h"
|
||||||
#include "sbank.h"
|
#include "sbank.h"
|
||||||
|
#include "srpc.h"
|
||||||
|
|
||||||
using namespace iop;
|
using namespace iop;
|
||||||
|
|
||||||
|
@ -44,21 +45,22 @@ int start_overlord(int argc, const char* const* argv) {
|
||||||
// return 1;
|
// return 1;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// thread_param.attr = TH_C;
|
thread_param.attr = TH_C;
|
||||||
// thread_param.initPriority = 99;
|
thread_param.initPriority = 99;
|
||||||
// thread_param.stackSize = 0x1000;
|
thread_param.stackSize = 0x1000;
|
||||||
// thread_param.option = 0;
|
thread_param.option = 0;
|
||||||
// thread_param.entry = Thread_Loader;
|
thread_param.entry = (void*)Thread_Loader;
|
||||||
// auto thread_loader = CreateThread(&thread_param);
|
strcpy(thread_param.name, "Loader"); // added for debug
|
||||||
// if(thread_loader <= 0) {
|
auto thread_loader = CreateThread(&thread_param);
|
||||||
// return 1;
|
if (thread_loader <= 0) {
|
||||||
// }
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
InitISOFS(argv[1], argv[2]);
|
InitISOFS(argv[1], argv[2]);
|
||||||
StartThread(thread_server, 0);
|
StartThread(thread_server, 0);
|
||||||
|
|
||||||
// StartThread(thread_player, 0);
|
// StartThread(thread_player, 0);
|
||||||
// StartThread(thread_loader, 0);
|
StartThread(thread_loader, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,63 @@
|
||||||
|
#include <cstring>
|
||||||
#include "sbank.h"
|
#include "sbank.h"
|
||||||
|
|
||||||
void InitBanks() {}
|
constexpr int N_BANKS = 3;
|
||||||
|
SoundBank* gBanks[N_BANKS];
|
||||||
|
SoundBank gCommonBank;
|
||||||
|
SoundBank gLevelBank[2];
|
||||||
|
|
||||||
|
void sbank_init_globals() {
|
||||||
|
gBanks[0] = &gCommonBank;
|
||||||
|
gBanks[1] = &gLevelBank[0];
|
||||||
|
gBanks[2] = &gLevelBank[1];
|
||||||
|
memset((void*)&gCommonBank, 0, sizeof(gCommonBank));
|
||||||
|
memset((void*)&gLevelBank, 0, sizeof(gLevelBank));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitBanks() {
|
||||||
|
for (auto& gBank : gBanks) {
|
||||||
|
gBank->unk1 = 0;
|
||||||
|
gBank->unk2 = 0;
|
||||||
|
strcpy(gBank->name, "<unused>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundBank* AllocateBank() {
|
||||||
|
int idx = 0;
|
||||||
|
// find a bank with unk1 = 0, or return nullptr if none exists
|
||||||
|
while (true) {
|
||||||
|
if (idx >= N_BANKS) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gBanks[idx]->unk1 == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx == 0) {
|
||||||
|
gBanks[0]->unk2 = 0x3fe; // ??
|
||||||
|
} else {
|
||||||
|
gBanks[idx]->unk2 = 0x65; // ??
|
||||||
|
}
|
||||||
|
|
||||||
|
return gBanks[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// name should be a 16-byte "sound name"
|
||||||
|
SoundBank* LookupBank(const char* name) {
|
||||||
|
int idx = N_BANKS - 1;
|
||||||
|
while (true) {
|
||||||
|
if (idx < 0) {
|
||||||
|
return nullptr; // not found.
|
||||||
|
}
|
||||||
|
auto& bank = gBanks[idx];
|
||||||
|
// they had some weird stuff here that took advantage of the fact that this region was
|
||||||
|
// 16-byte aligned, so it probably wasn't a memcmp, but this is easier.
|
||||||
|
if (memcmp(bank->name, name, 16) == 0) {
|
||||||
|
return bank;
|
||||||
|
}
|
||||||
|
idx--;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef JAK_V2_SBANK_H
|
#include "common/common_types.h"
|
||||||
#define JAK_V2_SBANK_H
|
|
||||||
|
struct SoundBank {
|
||||||
|
char name[16];
|
||||||
|
u32 unk1; // maybe status?
|
||||||
|
u32 unk2; // maybe size
|
||||||
|
};
|
||||||
|
|
||||||
|
void sbank_init_globals();
|
||||||
|
|
||||||
void InitBanks();
|
void InitBanks();
|
||||||
|
|
||||||
#endif // JAK_V2_SBANK_H
|
SoundBank* AllocateBank();
|
||||||
|
SoundBank* LookupBank(const char* name);
|
|
@ -1,8 +1,83 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include "common/util/assert.h"
|
||||||
#include "srpc.h"
|
#include "srpc.h"
|
||||||
|
#include "game/sce/iop.h"
|
||||||
|
#include "game/common/loader_rpc_types.h"
|
||||||
|
#include "common/versions.h"
|
||||||
|
#include "sbank.h"
|
||||||
|
#include "iso_api.h"
|
||||||
|
|
||||||
|
using namespace iop;
|
||||||
|
|
||||||
MusicTweaks gMusicTweakInfo;
|
MusicTweaks gMusicTweakInfo;
|
||||||
|
constexpr int SRPC_MESSAGE_SIZE = 0x50;
|
||||||
|
uint8_t gLoaderBuf[SRPC_MESSAGE_SIZE];
|
||||||
|
int32_t gSoundEnable = 1;
|
||||||
|
u32 gInfoEE = 0; // EE address where we should send info on each frame.
|
||||||
|
|
||||||
void srpc_init_globals() {
|
void srpc_init_globals() {
|
||||||
memset((void*)&gMusicTweakInfo, 0, sizeof(gMusicTweakInfo));
|
memset((void*)&gMusicTweakInfo, 0, sizeof(gMusicTweakInfo));
|
||||||
|
memset((void*)gLoaderBuf, 0, sizeof(gLoaderBuf));
|
||||||
|
gSoundEnable = 1;
|
||||||
|
gInfoEE = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo Thread_Player
|
||||||
|
|
||||||
|
void* RPC_Loader(unsigned int fno, void* data, int size);
|
||||||
|
|
||||||
|
u32 Thread_Loader() {
|
||||||
|
sceSifQueueData dq;
|
||||||
|
sceSifServeData serve;
|
||||||
|
|
||||||
|
// set up RPC
|
||||||
|
CpuDisableIntr();
|
||||||
|
sceSifInitRpc(0);
|
||||||
|
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||||
|
sceSifRegisterRpc(&serve, LOADER_RPC_ID, RPC_Loader, gLoaderBuf, nullptr, nullptr, &dq);
|
||||||
|
CpuEnableIntr();
|
||||||
|
sceSifRpcLoop(&dq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo RPC_Player
|
||||||
|
|
||||||
|
void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||||
|
int n_messages = size / SRPC_MESSAGE_SIZE;
|
||||||
|
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
|
||||||
|
if (gSoundEnable) {
|
||||||
|
// I don't think it should be possible to have > 1 message here - the buffer isn't big enough.
|
||||||
|
if (n_messages > 1) {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
while (n_messages > 0) {
|
||||||
|
switch (cmd->command) {
|
||||||
|
case SoundCommand::LOAD_BANK: {
|
||||||
|
// see if it's already loaded
|
||||||
|
auto bank = LookupBank(cmd->load_bank.bank_name);
|
||||||
|
if (!bank) {
|
||||||
|
// see if we have room to load another
|
||||||
|
auto new_bank = AllocateBank();
|
||||||
|
if (new_bank) {
|
||||||
|
// we do!
|
||||||
|
LoadSoundBank(cmd->load_bank.bank_name, new_bank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SoundCommand::GET_IRX_VERSION: {
|
||||||
|
cmd->irx_version.major = IRX_VERSION_MAJOR;
|
||||||
|
cmd->irx_version.minor = IRX_VERSION_MINOR;
|
||||||
|
gInfoEE = cmd->irx_version.ee_addr;
|
||||||
|
return cmd;
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
printf("Unhandled RPC Loader command %d\n", (int)cmd->command);
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
n_messages--;
|
||||||
|
cmd++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
|
@ -1,8 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef JAK_V2_SRPC_H
|
|
||||||
#define JAK_V2_SRPC_H
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
void srpc_init_globals();
|
void srpc_init_globals();
|
||||||
|
@ -18,6 +15,52 @@ struct MusicTweaks {
|
||||||
} MusicTweak[MUSIC_TWEAK_COUNT];
|
} MusicTweak[MUSIC_TWEAK_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SoundCommand : u16 {
|
||||||
|
LOAD_BANK = 0,
|
||||||
|
LOAD_MUSIC = 1,
|
||||||
|
UNLOAD_BANK = 2,
|
||||||
|
PLAY = 3,
|
||||||
|
PAUSE_SOUND = 4,
|
||||||
|
STOP_SOUND = 5,
|
||||||
|
CONTINUE_SOUND = 6,
|
||||||
|
SET_PARAM = 7,
|
||||||
|
SET_MASTER_VOLUME = 8,
|
||||||
|
PAUSE_GROUP = 9,
|
||||||
|
STOP_GROUP = 10,
|
||||||
|
CONTINUE_GROUP = 11,
|
||||||
|
GET_IRX_VERSION = 12,
|
||||||
|
SET_FALLOFF_CURVE = 13,
|
||||||
|
SET_SOUND_FALLOFF = 14,
|
||||||
|
RELOAD_INFO = 15,
|
||||||
|
SET_LANGUAGE = 16,
|
||||||
|
SET_FLAVA = 17,
|
||||||
|
SET_REVERB = 18,
|
||||||
|
SET_EAR_TRANS = 19,
|
||||||
|
SHUTDOWN = 20,
|
||||||
|
LIST_SOUNDS = 21,
|
||||||
|
UNLOAD_MUSIC = 22
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SoundRpcGetIrxVersion {
|
||||||
|
u32 major;
|
||||||
|
u32 minor;
|
||||||
|
u32 ee_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SoundRpcBankCommand {
|
||||||
|
u8 pad[12];
|
||||||
|
char bank_name[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SoundRpcCommand {
|
||||||
|
u16 rsvd1;
|
||||||
|
SoundCommand command;
|
||||||
|
union {
|
||||||
|
SoundRpcGetIrxVersion irx_version;
|
||||||
|
SoundRpcBankCommand load_bank;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
extern MusicTweaks gMusicTweakInfo;
|
extern MusicTweaks gMusicTweakInfo;
|
||||||
|
|
||||||
#endif // JAK_V2_SRPC_H
|
u32 Thread_Loader();
|
|
@ -46,6 +46,7 @@
|
||||||
#include "game/overlord/overlord.h"
|
#include "game/overlord/overlord.h"
|
||||||
#include "game/overlord/srpc.h"
|
#include "game/overlord/srpc.h"
|
||||||
#include "game/overlord/stream.h"
|
#include "game/overlord/stream.h"
|
||||||
|
#include "game/overlord/sbank.h"
|
||||||
|
|
||||||
#include "game/graphics/opengl.h"
|
#include "game/graphics/opengl.h"
|
||||||
#include "game/graphics/gfx.h"
|
#include "game/graphics/gfx.h"
|
||||||
|
@ -191,7 +192,7 @@ void iop_runner(SystemThreadInterface& iface) {
|
||||||
// isocommon
|
// isocommon
|
||||||
// overlord
|
// overlord
|
||||||
ramdisk_init_globals();
|
ramdisk_init_globals();
|
||||||
// sbank
|
sbank_init_globals();
|
||||||
// soundcommon
|
// soundcommon
|
||||||
srpc_init_globals();
|
srpc_init_globals();
|
||||||
// ssound
|
// ssound
|
||||||
|
|
|
@ -210,6 +210,9 @@ void IOP_Kernel::sif_rpc(s32 rpcChannel,
|
||||||
rec = &e;
|
rec = &e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!rec) {
|
||||||
|
printf("Failed to find handler for sif channel 0x%x\n", rpcChannel);
|
||||||
|
}
|
||||||
assert(rec);
|
assert(rec);
|
||||||
|
|
||||||
// step 2 - check entry is safe to give command to
|
// step 2 - check entry is safe to give command to
|
||||||
|
|
|
@ -104,9 +104,9 @@ struct DgoHeader {
|
||||||
(define-extern *load-dgo-rpc* rpc-buffer-pair)
|
(define-extern *load-dgo-rpc* rpc-buffer-pair)
|
||||||
(when (= 0 (the int *load-dgo-rpc*))
|
(when (= 0 (the int *load-dgo-rpc*))
|
||||||
;; we need to allocate the rpc buffers
|
;; we need to allocate the rpc buffers
|
||||||
(set! *load-dgo-rpc* (new 'global 'rpc-buffer-pair (the uint 32) (the uint 1) 3)) ;; todo, constants
|
(set! *load-dgo-rpc* (new 'global 'rpc-buffer-pair (the uint 32) (the uint 1) RPC-DGO))
|
||||||
(define *load-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 1) 4)) ;; todo, constants
|
(define *load-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 1) RPC-LOAD-STR))
|
||||||
(define *play-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 2) 5))
|
(define *play-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 2) RPC-PLAY-STR))
|
||||||
|
|
||||||
;; we have separate locks for queuing and loading.
|
;; we have separate locks for queuing and loading.
|
||||||
(define *load-str-lock* '#f)
|
(define *load-str-lock* '#f)
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
;; allocate the ramdisk RPC buffer
|
;; allocate the ramdisk RPC buffer
|
||||||
(define *ramdisk-rpc* (new 'global 'rpc-buffer-pair (the-as uint 32) (the-as uint 1) 2))
|
(define *ramdisk-rpc* (new 'global 'rpc-buffer-pair (the-as uint 32) (the-as uint 1) RPC-RAMDISK))
|
||||||
|
|
||||||
(define *current-ramdisk-id* 0)
|
(define *current-ramdisk-id* 0)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,15 @@
|
||||||
;; name in dgo: rpc-h
|
;; name in dgo: rpc-h
|
||||||
;; dgos: GAME, ENGINE
|
;; dgos: GAME, ENGINE
|
||||||
|
|
||||||
|
;; RPC channels.
|
||||||
|
;; these should match XXX_RPC_CHANNEL in the game/common/xxx_rpc_types.h
|
||||||
|
(defconstant RPC-SOUND-PLAYER 0) ;; called player in IOP code
|
||||||
|
(defconstant RPC-SOUND-LOADER 1) ;; called loader in IOP code
|
||||||
|
(defconstant RPC-RAMDISK 2) ;; called server in IOP code, sometimes
|
||||||
|
(defconstant RPC-DGO 3)
|
||||||
|
(defconstant RPC-LOAD-STR 4) ;; called STR in IOP code
|
||||||
|
(defconstant RPC-PLAY-STR 5) ;; called PLAY in IOP code
|
||||||
|
|
||||||
;; an RPC buffer is a container of elements to send to the IOP.
|
;; an RPC buffer is a container of elements to send to the IOP.
|
||||||
;; each element is size elt-size, and there are maximum of elt-count elements
|
;; each element is size elt-size, and there are maximum of elt-count elements
|
||||||
;; it is possible to use fewer elements than elt-count.
|
;; it is possible to use fewer elements than elt-count.
|
||||||
|
@ -136,7 +145,7 @@
|
||||||
"Call an RPC. This is an async RPC. Use check-busy or sync to see if it's done."
|
"Call an RPC. This is an async RPC. Use check-busy or sync to see if it's done."
|
||||||
(when (!= 0 (-> obj current elt-used))
|
(when (!= 0 (-> obj current elt-used))
|
||||||
;; when we have used elements
|
;; when we have used elements
|
||||||
(format 0 "call rpc-buffer-pair with ~D elts~%" (-> obj current elt-used))
|
;; (format 0 "call rpc-buffer-pair with ~D elts~%" (-> obj current elt-used))
|
||||||
|
|
||||||
;; make sure the previous buffer is done
|
;; make sure the previous buffer is done
|
||||||
(let ((active-buffer (if (= (-> obj buffer 0) (-> obj current))
|
(let ((active-buffer (if (= (-> obj buffer 0) (-> obj current))
|
||||||
|
@ -145,25 +154,21 @@
|
||||||
(when (-> active-buffer busy)
|
(when (-> active-buffer busy)
|
||||||
;; we think the active buffer may be busy.
|
;; we think the active buffer may be busy.
|
||||||
;; first lets just do a simple check
|
;; first lets just do a simple check
|
||||||
(cond
|
(when (!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||||
((!= 0 (rpc-busy? (-> obj rpc-port)))
|
;; busy! print an error and stall!
|
||||||
;; busy! print an error and stall!
|
(format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> obj rpc-port))
|
||||||
(format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> obj rpc-port))
|
(while (!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||||
(while (!= 0 (rpc-busy? (-> obj rpc-port)))
|
(+ 1 2 3)
|
||||||
(+ 1 2 3)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(else
|
|
||||||
;; not busy.
|
|
||||||
(set! (-> active-buffer busy) '#f)
|
|
||||||
(set! (-> active-buffer elt-used) 0)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
;; not busy.
|
||||||
|
(set! (-> active-buffer busy) '#f)
|
||||||
|
(set! (-> active-buffer elt-used) 0)
|
||||||
)
|
)
|
||||||
;; now we've cleared the last RPC call, we can do another
|
;; now we've cleared the last RPC call, we can do another
|
||||||
(let ((current-buffer (-> obj current)))
|
(let ((current-buffer (-> obj current)))
|
||||||
;; rpc_channel, fno, async, send_buff, send_size, recv_buff, recv_size
|
;; rpc_channel, fno, async, send_buff, send_size, recv_buff, recv_size
|
||||||
(format 0 "recv-size is ~D~%" recv-size)
|
;; (format 0 "recv-size is ~D~%" recv-size)
|
||||||
(rpc-call (-> obj rpc-port)
|
(rpc-call (-> obj rpc-port)
|
||||||
fno
|
fno
|
||||||
(the uint 1)
|
(the uint 1)
|
||||||
|
|
|
@ -31,6 +31,27 @@
|
||||||
:flag-assert #x900000010
|
:flag-assert #x900000010
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(defmacro static-sound-name (str)
|
||||||
|
"Convert a string constant to a static sound-name."
|
||||||
|
|
||||||
|
;; all this is done at compile-time so we can come up with 2
|
||||||
|
;; 64-bit constants to use
|
||||||
|
(when (> (string-length str) 16)
|
||||||
|
(error "static-sound-name got a string that is too long")
|
||||||
|
)
|
||||||
|
(let ((lo-val 0)
|
||||||
|
(hi-val 0)
|
||||||
|
)
|
||||||
|
(dotimes (i (string-length str))
|
||||||
|
(if (>= i 8)
|
||||||
|
(+! hi-val (ash (string-ref str i) (* 8 (- i 8))))
|
||||||
|
(+! lo-val (ash (string-ref str i) (* 8 i)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`(new 'static 'sound-name :lo ,lo-val :hi ,hi-val)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
(defenum sound-command
|
(defenum sound-command
|
||||||
:type uint16
|
:type uint16
|
||||||
(load-bank)
|
(load-bank)
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
;; dgos: GAME, ENGINE
|
;; dgos: GAME, ENGINE
|
||||||
|
|
||||||
|
|
||||||
(define *sound-player-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 128) 0))
|
(define *sound-player-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 128) RPC-SOUND-PLAYER))
|
||||||
(define *sound-loader-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 1) 1))
|
(define *sound-loader-rpc* (new 'global 'rpc-buffer-pair (the-as uint 80) (the-as uint 1) RPC-SOUND-LOADER))
|
||||||
|
|
||||||
(defun sound-name= ((arg0 sound-name) (arg1 sound-name))
|
(defun sound-name= ((arg0 sound-name) (arg1 sound-name))
|
||||||
"Return #t if both are the same name"
|
"Return #t if both are the same name"
|
||||||
|
@ -101,8 +101,7 @@
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
;; TODO rpc 1 unimplemented
|
(check-irx-version)
|
||||||
;(check-irx-version)
|
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -146,10 +145,10 @@
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
;; TODO rpc 1 unimplemented
|
(sound-bank-load (static-sound-name "common"))
|
||||||
;(sound-bank-load (new 'static 'sound-name :lo #x6e6f6d6d6f63 :hi 0)) ;; common
|
(sound-bank-load (static-sound-name "empty1"))
|
||||||
;(sound-bank-load (new 'static 'sound-name :lo #x317974706d65 :hi 0)) ;; empty1
|
(sound-bank-load (static-sound-name "empty2"))
|
||||||
;(sound-bank-load (new 'static 'sound-name :lo #x327974706d65 :hi 0)) ;; empty2
|
|
||||||
(define *sound-bank-1* 'empty1)
|
(define *sound-bank-1* 'empty1)
|
||||||
(define *sound-bank-2* 'empty2)
|
(define *sound-bank-2* 'empty2)
|
||||||
|
|
||||||
|
@ -302,22 +301,20 @@
|
||||||
|
|
||||||
(defun string->sound-name ((str string))
|
(defun string->sound-name ((str string))
|
||||||
(let ((snd-name (new 'stack-no-clear 'qword)))
|
(let ((snd-name (new 'stack-no-clear 'qword)))
|
||||||
(set! (-> snd-name quad) (the-as uint128 0))
|
(set! (-> snd-name quad) (the-as uint128 0))
|
||||||
(let ((out-ptr (the-as (pointer uint8) snd-name))
|
(let ((out-ptr (the-as (pointer uint8) snd-name))
|
||||||
(in-ptr (-> str data))
|
(in-ptr (-> str data))
|
||||||
)
|
)
|
||||||
(while
|
(while (and (nonzero? (-> in-ptr 0))
|
||||||
(and
|
(< (&- in-ptr (the-as uint (-> str data))) 15)
|
||||||
(nonzero? (-> in-ptr 0))
|
)
|
||||||
(< (&- in-ptr (the-as uint (-> str data))) 15)
|
(set! (-> out-ptr 0) (-> in-ptr 0))
|
||||||
|
(set! out-ptr (&-> out-ptr 1))
|
||||||
|
(set! in-ptr (&-> in-ptr 1))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
(set! (-> out-ptr 0) (-> in-ptr 0))
|
(the-as sound-name (-> snd-name quad))
|
||||||
(set! out-ptr (&-> out-ptr 1))
|
|
||||||
(set! in-ptr (&-> in-ptr 1))
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
(the-as sound-name (-> snd-name quad))
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
(defun sound-set-volume ((group uint) (volume float))
|
(defun sound-set-volume ((group uint) (volume float))
|
||||||
|
|
|
@ -22,7 +22,10 @@
|
||||||
|
|
||||||
(defmacro md (file &rest path)
|
(defmacro md (file &rest path)
|
||||||
"Make Debug: make + print disassembly for a file"
|
"Make Debug: make + print disassembly for a file"
|
||||||
`(asm-file ,file :color :write :disassemble ,(first path))
|
(if (null? path)
|
||||||
|
`(asm-file ,file :color :write :disassemble)
|
||||||
|
`(asm-file ,file :color :write :disassemble ,(first path))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
(defmacro ml (file)
|
(defmacro ml (file)
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
`(cond (,clause ,true) (#t ,false))
|
`(cond (,clause ,true) (#t ,false))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(defsmacro when (clause &rest body)
|
||||||
|
`(if ,clause (begin ,@body) #f)
|
||||||
|
)
|
||||||
|
|
||||||
(defsmacro not (x)
|
(defsmacro not (x)
|
||||||
`(if ,x #f #t)
|
`(if ,x #f #t)
|
||||||
)
|
)
|
||||||
|
@ -34,6 +38,14 @@
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(defsmacro max (a b)
|
||||||
|
`(if (> a b) a b)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defsmacro min (a b)
|
||||||
|
`(if (< a b) a b)
|
||||||
|
)
|
||||||
|
|
||||||
(defsmacro caar (x)
|
(defsmacro caar (x)
|
||||||
`(car (car ,x)))
|
`(car (car ,x)))
|
||||||
|
|
||||||
|
@ -96,6 +108,16 @@
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(defsmacro dotimes (var &rest body)
|
||||||
|
`(let (( ,(first var) 0))
|
||||||
|
(while (< ,(first var) ,(second var))
|
||||||
|
,@body
|
||||||
|
(set! ,(first var) (+ ,(first var) 1))
|
||||||
|
)
|
||||||
|
,@(cddr var)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
(desfun repeated-list (obj count)
|
(desfun repeated-list (obj count)
|
||||||
(if (= 0 count)
|
(if (= 0 count)
|
||||||
'()
|
'()
|
||||||
|
@ -169,5 +191,13 @@
|
||||||
`(seval (desfun ,name ,args ,@body))
|
`(seval (desfun ,name ,args ,@body))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; shortcut to quit GOOS
|
||||||
|
(defsmacro e ()
|
||||||
|
`(exit)
|
||||||
|
)
|
||||||
|
|
||||||
;; this is checked in a test to see if this file is loaded.
|
;; this is checked in a test to see if this file is loaded.
|
||||||
(define __goos-lib-loaded__ #t)
|
(define __goos-lib-loaded__ #t)
|
||||||
|
|
|
@ -1174,6 +1174,83 @@ TEST_F(FormRegressionTest, Method4ResTag) {
|
||||||
test_with_expr(func, type, expected);
|
test_with_expr(func, type, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FormRegressionTest, MakeSqrtTable) {
|
||||||
|
std::string func =
|
||||||
|
"sll r0, r0, 0\n"
|
||||||
|
"L149:\n"
|
||||||
|
" daddiu sp, sp, -32\n"
|
||||||
|
" sd ra, 0(sp)\n"
|
||||||
|
" sd fp, 8(sp)\n"
|
||||||
|
" or fp, t9, r0\n"
|
||||||
|
" sq gp, 16(sp)\n"
|
||||||
|
" lw t9, format(s7)\n"
|
||||||
|
" daddiu a0, s7, #t\n"
|
||||||
|
" daddiu a1, fp, L190\n"
|
||||||
|
" jalr ra, t9\n"
|
||||||
|
" sll v0, ra, 0\n"
|
||||||
|
" addiu gp, r0, 0\n"
|
||||||
|
" beq r0, r0, L151\n"
|
||||||
|
" sll r0, r0, 0\n"
|
||||||
|
"L150:\n"
|
||||||
|
//" lwc1 f0, L232(fp)\n"
|
||||||
|
" mtc1 f0, r0\n"
|
||||||
|
" mtc1 f1, gp\n"
|
||||||
|
" cvt.s.w f1, f1\n"
|
||||||
|
" mul.s f0, f0, f1\n"
|
||||||
|
" sqrt.s f0, f0\n"
|
||||||
|
//" lwc1 f1, L233(fp)\n"
|
||||||
|
" mtc1 f1, r0\n"
|
||||||
|
" add.s f0, f1, f0\n"
|
||||||
|
" cvt.w.s f0, f0\n"
|
||||||
|
" mfc1 a2, f0\n"
|
||||||
|
" lw t9, format(s7)\n"
|
||||||
|
" daddiu a0, s7, #t\n"
|
||||||
|
" daddiu a1, fp, L189\n"
|
||||||
|
" jalr ra, t9\n"
|
||||||
|
" sll v0, ra, 0\n"
|
||||||
|
" or v1, v0, r0\n"
|
||||||
|
" daddiu gp, gp, 1\n"
|
||||||
|
"L151:\n"
|
||||||
|
" slti v1, gp, 256\n"
|
||||||
|
" bne v1, r0, L150\n"
|
||||||
|
" sll r0, r0, 0\n"
|
||||||
|
|
||||||
|
" or v1, s7, r0\n"
|
||||||
|
" or v1, s7, r0\n"
|
||||||
|
" lw t9, format(s7)\n"
|
||||||
|
" daddiu a0, s7, #t\n"
|
||||||
|
" daddiu a1, fp, L188\n"
|
||||||
|
" jalr ra, t9\n"
|
||||||
|
" sll v0, ra, 0\n"
|
||||||
|
" or v0, r0, r0\n"
|
||||||
|
" ld ra, 0(sp)\n"
|
||||||
|
" ld fp, 8(sp)\n"
|
||||||
|
" lq gp, 16(sp)\n"
|
||||||
|
" jr ra\n"
|
||||||
|
" daddiu sp, sp, 32\n"
|
||||||
|
" sll r0, r0, 0\n"
|
||||||
|
" sll r0, r0, 0";
|
||||||
|
std::string type = "(function none)";
|
||||||
|
std::string expected =
|
||||||
|
"(begin\n"
|
||||||
|
" (format #t \"static int sqrt_table[256] =~%{~%\")\n"
|
||||||
|
" (dotimes (gp-0 256)\n"
|
||||||
|
" (let* ((f0-2 (sqrtf (* 0.0 (the float gp-0))))\n"
|
||||||
|
" (a2-0 (the int (+ 0.0 f0-2)))\n"
|
||||||
|
" )\n"
|
||||||
|
" (format #t \"~D,~%\" a2-0)\n"
|
||||||
|
" )\n"
|
||||||
|
" )\n"
|
||||||
|
" (format #t \"};~%\")\n"
|
||||||
|
" (let ((v0-3 0))\n"
|
||||||
|
" )\n"
|
||||||
|
" (none)\n"
|
||||||
|
" )";
|
||||||
|
test_with_expr(
|
||||||
|
func, type, expected, false, "",
|
||||||
|
{{"L190", "static int sqrt_table[256] =~%{~%"}, {"L189", "~D,~%"}, {"L188", "};~%"}});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(FormRegressionTest, Method2Vec4s) {
|
TEST_F(FormRegressionTest, Method2Vec4s) {
|
||||||
std::string func =
|
std::string func =
|
||||||
"sll r0, r0, 0\n"
|
"sll r0, r0, 0\n"
|
||||||
|
@ -1219,3 +1296,24 @@ TEST_F(FormRegressionTest, Method2Vec4s) {
|
||||||
" )";
|
" )";
|
||||||
test_with_expr(func, type, expected, false, "", {{"L344", "#<vector ~F ~F ~F ~F @ #x~X>"}});
|
test_with_expr(func, type, expected, false, "", {{"L344", "#<vector ~F ~F ~F ~F @ #x~X>"}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FormRegressionTest, SoundNameEqual) {
|
||||||
|
std::string func =
|
||||||
|
"sll r0, r0, 0\n"
|
||||||
|
" dsubu v1, a0, a1\n"
|
||||||
|
" daddiu a2, s7, 8\n"
|
||||||
|
" movn a2, s7, v1\n"
|
||||||
|
" beql s7, a2, L125\n"
|
||||||
|
" or v0, a2, r0\n"
|
||||||
|
" pcpyud v1, a0, r0\n"
|
||||||
|
" pcpyud a0, a1, r0\n"
|
||||||
|
" dsubu v1, v1, a0\n"
|
||||||
|
" daddiu v0, s7, 8\n"
|
||||||
|
" movn v0, s7, v1\n"
|
||||||
|
"L125:\n"
|
||||||
|
" jr ra\n"
|
||||||
|
" daddu sp, sp, r0";
|
||||||
|
std::string type = "(function sound-name sound-name symbol)";
|
||||||
|
std::string expected = "(and (= arg0 arg1) (= (-> arg0 hi) (-> arg1 hi)))";
|
||||||
|
test_with_expr(func, type, expected);
|
||||||
|
}
|
12
test/goalc/source_templates/with_game/test-sound-name.gc
Normal file
12
test/goalc/source_templates/with_game/test-sound-name.gc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
(let ((sound-name-1 (static-sound-name "asdfghjkzxcvn,."))
|
||||||
|
(sound-name-2 (string->sound-name "asdfghjkzxcvn,."))
|
||||||
|
(sound-name-3 (string->sound-name "asdfghjkzxcvnn,."))
|
||||||
|
(sound-name-4 (string->sound-name "asddghjkzxcvnm,."))
|
||||||
|
)
|
||||||
|
(format #t "~A ~A ~A~%"
|
||||||
|
(sound-name= sound-name-1 sound-name-2)
|
||||||
|
(sound-name= sound-name-1 sound-name-3)
|
||||||
|
(sound-name= sound-name-1 sound-name-4)
|
||||||
|
)
|
||||||
|
)
|
|
@ -753,6 +753,12 @@ TEST_F(WithGameTests, AddrOfVar) {
|
||||||
"0\n"});
|
"0\n"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WithGameTests, SoundName) {
|
||||||
|
runner.run_static_test(env, testCategory, "test-sound-name.gc",
|
||||||
|
{"#t #f #f\n"
|
||||||
|
"0\n"});
|
||||||
|
}
|
||||||
|
|
||||||
TEST(TypeConsistency, TypeConsistency) {
|
TEST(TypeConsistency, TypeConsistency) {
|
||||||
Compiler compiler;
|
Compiler compiler;
|
||||||
compiler.enable_throw_on_redefines();
|
compiler.enable_throw_on_redefines();
|
||||||
|
|
Loading…
Reference in a new issue