[Decompiler] Fixes for dma-disasm (#377)

* small tweaks

* fix up some more dma stuff
This commit is contained in:
water111 2021-04-23 20:29:15 -04:00 committed by GitHub
parent 0b8a878533
commit fa122356ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 467 additions and 433 deletions

View file

@ -89,7 +89,6 @@ EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts) {
iter = cdr(iter);
auto option_value = car(iter);
iter = cdr(iter);
current = car(iter);
if (option_name == ":type") {
base_type = parse_typespec(ts, option_value);
@ -102,10 +101,28 @@ EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts) {
fmt::print("Invalid option {} to :bitfield option.\n", option_value.print());
throw std::runtime_error("invalid bitfield option");
}
} else if (option_name == ":copy-entries") {
auto other_info = ts->try_enum_lookup(parse_typespec(ts, option_value));
if (!other_info) {
throw std::runtime_error(fmt::format(
"Cannot copy entries from {}, it is not a valid enum type", option_value.print()));
}
for (auto& e : other_info->entries()) {
if (entries.find(e.first) != entries.end()) {
throw std::runtime_error(fmt::format("Entry {} appears multiple times.", e.first));
}
entries[e.first] = e.second;
}
} else {
fmt::print("Unknown option {} for defenum.\n", option_name);
throw std::runtime_error("unknown option for defenum");
}
if (iter->is_pair()) {
current = car(iter);
} else {
break;
}
}
auto type = ts->lookup_type(base_type);
@ -128,6 +145,10 @@ EnumType* parse_defenum(const goos::Object& defenum, TypeSystem* ts) {
fmt::print("Got too many items in defenum {} entry {}\n", name, entry_name);
}
if (entries.find(entry_name) != entries.end()) {
throw std::runtime_error(fmt::format("Entry {} appears multiple times.", entry_name));
}
entries[entry_name] = entry_val;
iter = cdr(iter);
}

View file

@ -137,25 +137,14 @@ void Function::analyze_prologue(const LinkedObjectFile& file) {
while (is_no_link_gpr_store(instructions.at(gpr_idx), 16, {}, {}, make_gpr(Reg::SP))) {
auto store_reg = instructions.at(gpr_idx).get_src(0).get_reg();
// sometimes stack memory is zeroed immediately after gpr backups, and this fools the previous
// check.
if (store_reg == make_gpr(Reg::R0)) {
lg::warn("Function {} has stack zeroing, manually check prologue!",
guessed_name.to_string());
warnings.general_warning("Stack zeroing, check prologue!");
// sometimes stack memory is zeroed or a register is spilled immediately after gpr backups,
// and this fools the previous check.
if (store_reg == make_gpr(Reg::R0) || store_reg == make_gpr(Reg::A0)) {
warnings.general_warning("Check prologue - tricky store of {}", store_reg.to_string());
expect_nothing_after_gprs = true;
break;
}
// this also happens a few times per game. this a0/r0 check seems to be all that's needed to
// avoid false positives here!
if (store_reg == make_gpr(Reg::A0)) {
suspected_asm = true;
lg::warn("Function {} stores a0 on the stack, flagging as asm!", guessed_name.to_string());
warnings.general_warning("Flagged as asm due to storing a0 on stack");
return;
}
n_gpr_backups++;
gpr_idx++;
}

View file

@ -484,6 +484,12 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
rd_in.deref = dk;
rd_in.base_type = input_type.get_obj_plus_const_mult_typespec();
rd_in.stride = input_type.get_multiplier();
// this is a bit of a hack to prevent something like arr_of_bytes[val * 4] getting stuck
// with the stride of 4 bytes but load size of 1 byte. I _believe_ it only applies when
// the load size is 1.
if (rd_in.base_type == env.dts->ts.make_pointer_typespec("uint8") && rd_in.stride != 0) {
rd_in.stride = 1;
}
rd_in.offset = ro.offset;
auto rd = env.dts->ts.reverse_field_lookup(rd_in);
@ -496,8 +502,8 @@ Form* LoadVarOp::get_load_src(FormPool& pool, const Env& env) const {
// we pass along the register offset because code generation seems to be a bit
// different in different cases.
return pool.alloc_single_element_form<ArrayFieldAccess>(
nullptr, ro.var, tokens, input_type.get_multiplier(), ro.offset);
return pool.alloc_single_element_form<ArrayFieldAccess>(nullptr, ro.var, tokens,
rd_in.stride, ro.offset);
}
}

View file

@ -419,6 +419,9 @@ TP_Type SimpleExpression::get_type_int2(const TypeState& input,
if ((m_kind == Kind::ADD || m_kind == Kind::SUB) &&
arg0_type.typespec().base_type() == "pointer" && tc(dts, TypeSpec("integer"), arg1_type)) {
if (m_kind == Kind::ADD && !m_args[1].is_int()) {
return TP_Type::make_object_plus_product(arg0_type.typespec(), 1);
}
// plain pointer plus integer = plain pointer
return TP_Type::make_from_ts(arg0_type.typespec());
}

View file

@ -217,8 +217,12 @@ Form* cast_form(Form* in, const TypeSpec& new_type, FormPool& pool, const Env& e
}
auto enum_info = dynamic_cast<EnumType*>(type_info);
if (enum_info && enum_info->is_bitfield()) {
return cast_to_bitfield_enum(enum_info, new_type, pool, env, in);
if (enum_info) {
if (enum_info->is_bitfield()) {
return cast_to_bitfield_enum(enum_info, new_type, pool, env, in);
} else {
return cast_to_int_enum(enum_info, new_type, pool, env, in);
}
}
return pool.alloc_single_element_form<CastElement>(nullptr, new_type, in);
@ -2326,10 +2330,10 @@ FormElement* ConditionElement::make_nonzero_check_generic(const Env& env,
}
}
FormElement* ConditionElement::make_equal_check_generic(const Env&,
FormElement* ConditionElement::make_equal_check_generic(const Env& env,
FormPool& pool,
const std::vector<Form*>& source_forms,
const std::vector<TypeSpec>&) {
const std::vector<TypeSpec>& source_types) {
assert(source_forms.size() == 2);
// (= thing '())
auto ref = source_forms.at(1);
@ -2339,8 +2343,18 @@ FormElement* ConditionElement::make_equal_check_generic(const Env&,
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::NULLP),
source_forms.at(0));
} else {
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::EQ),
source_forms);
auto int_val = get_goal_integer_constant(source_forms.at(1), env);
auto src0_as_enum = env.dts->ts.try_enum_lookup(source_types.at(0));
if (src0_as_enum && int_val) {
// if comparing an enum against a constant integer, rewrite the enum.
auto forms_with_cast = source_forms;
forms_with_cast.at(1) = cast_form(source_forms.at(1), source_types.at(0), pool, env);
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::EQ),
forms_with_cast);
} else {
return pool.alloc_element<GenericElement>(GenericOperator::make_fixed(FixedOperatorKind::EQ),
source_forms);
}
}
}
@ -2812,7 +2826,37 @@ void ArrayFieldAccess::update_with_val(Form* new_val,
if (m_constant_offset == 0) {
if (m_expected_stride == 1) {
throw std::runtime_error("One case, not yet implemented (no offset)");
auto base_matcher =
Matcher::match_or({Matcher::cast("int", Matcher::any(0)),
Matcher::cast("uint", Matcher::any(0)), Matcher::any(0)});
auto offset_matcher =
Matcher::match_or({Matcher::cast("int", Matcher::any(1)),
Matcher::cast("uint", Matcher::any(1)), Matcher::any(1)});
// (&+ data-ptr <idx>)
auto matcher = Matcher::match_or(
{Matcher::fixed_op(FixedOperatorKind::ADDITION, {base_matcher, offset_matcher}),
Matcher::fixed_op(FixedOperatorKind::ADDITION_PTR, {base_matcher, offset_matcher})});
auto match_result = match(matcher, new_val);
if (!match_result.matched) {
throw std::runtime_error(
fmt::format("Failed to match array stride 1 load {}", new_val->to_string(env)));
}
auto idx = match_result.maps.forms.at(1);
auto base = match_result.maps.forms.at(0);
assert(idx && base);
std::vector<DerefToken> tokens = m_deref_tokens;
for (auto& x : tokens) {
if (x.kind() == DerefToken::Kind::EXPRESSION_PLACEHOLDER) {
x = DerefToken::make_int_expr(idx);
}
}
// tokens.push_back(DerefToken::make_int_expr(idx));
auto deref = pool.alloc_element<DerefElement>(base, false, tokens);
result->push_back(deref);
} else if (is_power_of_two(m_expected_stride, &power_of_two)) {
// reg0 is base
// reg1 is idx

View file

@ -478,6 +478,7 @@ Form* cast_to_bitfield_enum(const EnumType* type_info,
FormPool& pool,
const Env& env,
Form* in) {
assert(type_info->is_bitfield());
auto integer = get_goal_integer_constant(strip_int_or_uint_cast(in), env);
if (integer) {
auto elts =
@ -495,4 +496,24 @@ Form* cast_to_bitfield_enum(const EnumType* type_info,
}
}
Form* cast_to_int_enum(const EnumType* type_info,
const TypeSpec& typespec,
FormPool& pool,
const Env& env,
Form* in) {
assert(!type_info->is_bitfield());
auto integer = get_goal_integer_constant(strip_int_or_uint_cast(in), env);
if (integer) {
auto entry =
decompile_int_enum_from_int(TypeSpec(type_info->get_name()), env.dts->ts, *integer);
auto oper = GenericOperator::make_function(
pool.alloc_single_element_form<ConstantTokenElement>(nullptr, type_info->get_name()));
return pool.alloc_single_element_form<GenericElement>(
nullptr, oper, pool.alloc_single_element_form<ConstantTokenElement>(nullptr, entry));
} else {
// all failed, just return whatever.
return pool.alloc_single_element_form<CastElement>(nullptr, typespec, in);
}
}
} // namespace decompiler

View file

@ -144,5 +144,11 @@ Form* cast_to_bitfield_enum(const EnumType* type_info,
const Env& env,
Form* in);
Form* cast_to_int_enum(const EnumType* type_info,
const TypeSpec& typespec,
FormPool& pool,
const Env& env,
Form* in);
std::optional<u64> get_goal_integer_constant(Form* in, const Env&);
} // namespace decompiler

View file

@ -42,7 +42,7 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) {
ir2_register_usage_pass();
lg::info("Variable analysis...");
ir2_variable_pass();
lg::info("Initial structuring..");
lg::info("Initial structuring...");
ir2_cfg_build_pass();
if (get_config().analyze_expressions) {
lg::info("Storing temporary form result...");

View file

@ -163,7 +163,31 @@ std::string write_from_top_level(const Function& top_level,
return ";; ERROR: top level has no register use analysis. Cannot decompile.\n\n";
}
std::vector<FormElement*> forms = top_form->elts();
assert(!forms.empty());
// remove a (none) from the end, if it exists.
auto back_as_generic_op = dynamic_cast<GenericElement*>(forms.back());
if (back_as_generic_op && back_as_generic_op->op().is_fixed(FixedOperatorKind::NONE)) {
forms.pop_back();
}
std::string result;
// look for the whole thing being in a (when *debug-segment* ....)
bool in_debug_only_file = false;
if (forms.size() == 1) {
auto as_cne = dynamic_cast<CondNoElseElement*>(forms.at(0));
if (as_cne && as_cne->entries.size() == 1) {
auto& entry = as_cne->entries.at(0);
// a bit gross...
if (entry.condition->to_string(env) == "*debug-segment*") {
forms = entry.body->elts();
result += ";; this file is debug only\n";
result += "(when *debug-segment*\n";
in_debug_only_file = true;
}
}
}
// (set! identity L312)
constexpr int func_name = 1;
@ -195,7 +219,7 @@ std::string write_from_top_level(const Function& top_level,
// (set! sym-val <expr>)
auto define_symbol_matcher = Matcher::set(Matcher::any_symbol(0), Matcher::any(1));
for (auto& x : top_form->elts()) {
for (auto& x : forms) {
bool something_matched = false;
Form f;
f.elts().push_back(x);
@ -291,6 +315,10 @@ std::string write_from_top_level(const Function& top_level,
}
}
if (in_debug_only_file) {
result += ")\n";
}
return result;
}
} // namespace decompiler

View file

@ -2365,14 +2365,27 @@
:flag-assert #x900000014
)
(defenum dma-tag-id
:bitfield #f
:type uint8
(refe 0) ;; addr=ADDR, ends after this transfer
(cnt 1) ;; addr=after tag, next-tag=after data
(next 2) ;; addr=after tag, next-tag=ADDR
(ref 3) ;; addr=ADDR, next-tag=after tag
(refs 4) ;; ref, but stall controled
(call 5) ;;
(ret 6) ;;
(end 7) ;; next, but ends.
)
; ;; dma-h
(deftype dma-tag (uint64)
((qwc uint16 :offset 0) ;; quadword count
(pce uint8 :offset 26 :size 2) ;; priority (source mode)
(id uint8 :offset 28 :size 3) ;; ID (what the tag means)
(irq uint8 :offset 31 :size 1) ;; interrupt at the end?
(addr uint32 :offset 32 :size 31) ;; address (31 bits)
(spr uint8 :offset 63 :size 1) ;; spr or not flag.
((qwc uint16 :offset 0) ;; quadword count
(pce uint8 :offset 26 :size 2) ;; priority (source mode)
(id dma-tag-id :offset 28 :size 3) ;; ID (what the tag means)
(irq uint8 :offset 31 :size 1) ;; interrupt at the end?
(addr uint32 :offset 32 :size 31) ;; address (31 bits)
(spr uint8 :offset 63 :size 1) ;; spr or not flag.
)
:method-count-assert 9
:size-assert #x8
@ -2391,13 +2404,61 @@
:flag-assert #x900000010
)
;; all these have mask (only applies to unpacks) and interrupt not set.
(defenum vif-cmd
:bitfield #f
:type uint8
(nop 0) ;; no-op, can still have irq set.
(stcycl 1) ;; set write recycle register
(offset 2) ;; set offset register
(base 3) ;; set base register
(itop 4) ;; set data pointer register (itops)
(stmod 5) ;; set mode register
(mskpath3 6) ;; set path 3 mask
(mark 7) ;; set mark register
(flushe 16) ;; wait for end of microprogram
(flush 17) ;; wait for end of microprogram and transfer (path1/path2)
(flusha 19) ;; wait for end of microprogram and transfer (path1/path2/path3)
(mscal 20) ;; activate microprogram (call)
(mscalf 21) ;; flushe and activate (call)
(mscnt 23) ;; activate microprogram (continue)
(stmask 32) ;; set MASK register.
(strow 48) ;; set filling data
(stcol 49) ;; set filling data
(mpg 74) ;; transfer microprogram
(direct 80) ;; straight to GIF.
(directhl 81)
(unpack-s-32 96)
(unpack-s-16 97)
(unpack-s-8 98)
;; 99 is invllid
(unpack-v2-32 100)
(unpack-v2-16 101)
(unpack-v2-8 102)
;; 103 is invalid
(unpack-v3-32 104)
(unpack-v3-16 105)
(unpack-v3-8 106)
;; 107 is invalid
(unpack-v4-32 108)
(unpack-v4-16 109)
(unpack-v4-8 110)
(unpack-v4-5 111)
)
(defenum vif-cmd-32
:bitfield #f
:type uint32
:copy-entries vif-cmd
)
;; dma-h
(deftype vif-tag (uint32)
((imm uint16 :offset 0 :size 16)
(num uint8 :offset 16 :size 8)
(cmd uint8 :offset 24 :size 7)
(irq uint8 :offset 31 :size 1)
(msk uint8 :offset 28 :size 1)
((imm uint16 :offset 0 :size 16)
(num uint8 :offset 16 :size 8)
(cmd vif-cmd :offset 24 :size 7)
(irq uint8 :offset 31 :size 1)
(msk uint8 :offset 28 :size 1)
)
:method-count-assert 9
:size-assert #x4
@ -2698,10 +2759,10 @@
(deftype vif-disasm-element (structure)
((mask uint32 :offset-assert 0)
(tag uint32 :offset-assert 4)
(val uint32 :offset-assert 8)
(print uint32 :offset-assert 12)
((mask uint32 :offset-assert 0)
(tag vif-cmd-32 :offset-assert 4)
(val uint32 :offset-assert 8)
(print uint32 :offset-assert 12)
(string1 string :offset-assert 16)
(string2 string :offset-assert 20)
)
@ -2712,10 +2773,10 @@
(define-extern *vif-disasm-table* (array vif-disasm-element)) ;; unknown type
;;(define-extern disasm-vif-tag (function (pointer uint32) int symbol int symbol))
(define-extern disasm-dma-tag (function uint symbol int))
(define-extern disasm-vif-details (function symbol (pointer uint8) int int symbol))
(define-extern disasm-dma-tag (function dma-tag symbol int))
(define-extern disasm-vif-details (function symbol (pointer uint8) vif-cmd int symbol))
(define-extern vif-disasm-element type)
;;(define-extern *dma-disasm* object) ;; unknown type
(define-extern *dma-disasm* symbol)
(define-extern disasm-dma-list function)
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;

View file

@ -157,9 +157,6 @@
"symlink2", // F: asm branching
"dma-sync-hang",
// dma-disasm (BUG)
"disasm-dma-list",
// display
"vblank-handler", // F: weird asm for interrupt handler
"vif1-handler", // F: weird asm for interrupt handler

View file

@ -173,6 +173,14 @@
[3, "a0", "dma-bucket"]
],
"disasm-vif-details": [
[[62, 94], "s3", "(pointer uint32)"],
[[98, 130], "s3", "(pointer uint16)"],
[[134, 164], "s3", "(pointer uint32)"],
[[168, 198], "s3", "(pointer uint16)"],
[[202, 225], "s3", "(pointer uint16)"]
],
// LEVEL
"lookup-level-info": [
[3, "a1", "symbol"],

View file

@ -131,7 +131,7 @@ TypeSpec TP_Type::typespec() const {
return m_ts;
case Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT:
if (m_ts.base_type() == "pointer") {
return TypeSpec("pointer");
return m_ts;
}
// this can be part of an array access, so we don't really know the type.
// probably not a good idea to try to do anything with this as a typespec

View file

@ -59,10 +59,11 @@ class TP_Type {
case Kind::NON_VIRTUAL_METHOD:
return false;
case Kind::UNINITIALIZED:
case Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT:
case Kind::OBJECT_NEW_METHOD:
case Kind::DYNAMIC_METHOD_ACCESS:
return true;
case Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT:
return m_ts.base_type() != "pointer";
case Kind::INVALID:
default:
assert(false);

View file

@ -526,21 +526,55 @@ goos::Object decompile_structure(const TypeSpec& type,
return pretty_print::build_list(result_def);
}
namespace {
goos::Object bitfield_defs_print(const TypeSpec& type,
const std::vector<BitFieldConstantDef>& defs) {
std::vector<goos::Object> result;
result.push_back(pretty_print::to_symbol(fmt::format("new 'static '{}", type.print())));
for (auto& def : defs) {
if (def.is_signed) {
result.push_back(
pretty_print::to_symbol(fmt::format(":{} {}", def.field_name, (s64)def.value)));
} else {
result.push_back(
pretty_print::to_symbol(fmt::format(":{} #x{:x}", def.field_name, def.value)));
}
}
return pretty_print::build_list(result);
}
} // namespace
goos::Object decompile_value(const TypeSpec& type,
const std::vector<u8>& bytes,
const TypeSystem& ts) {
auto bitfield_enum = ts.try_enum_lookup(type);
if (bitfield_enum) {
assert((int)bytes.size() == bitfield_enum->get_load_size());
auto as_enum = ts.try_enum_lookup(type);
if (as_enum) {
assert((int)bytes.size() == as_enum->get_load_size());
assert(bytes.size() <= 8);
u64 value = 0;
memcpy(&value, bytes.data(), bytes.size());
auto defs = decompile_bitfield_enum_from_int(type, ts, value);
std::vector<goos::Object> result_def = {pretty_print::to_symbol(type.print())};
for (auto& x : defs) {
result_def.push_back(pretty_print::to_symbol(x));
if (as_enum->is_bitfield()) {
auto defs = decompile_bitfield_enum_from_int(type, ts, value);
std::vector<goos::Object> result_def = {pretty_print::to_symbol(type.print())};
for (auto& x : defs) {
result_def.push_back(pretty_print::to_symbol(x));
}
return pretty_print::build_list(result_def);
} else {
auto def = decompile_int_enum_from_int(type, ts, value);
return pretty_print::build_list(type.print(), def);
}
return pretty_print::build_list(result_def);
}
auto as_bitfield = dynamic_cast<BitFieldType*>(ts.lookup_type(type));
if (as_bitfield) {
assert((int)bytes.size() == as_bitfield->get_load_size());
assert(bytes.size() <= 8);
u64 value = 0;
memcpy(&value, bytes.data(), bytes.size());
auto defs = decompile_bitfield_from_int(type, ts, value);
return bitfield_defs_print(type, defs);
}
// try as common integer types:
@ -802,20 +836,7 @@ goos::Object decompile_bitfield(const TypeSpec& type,
// read as u64
u64 value = *(u64*)(elt_bytes.data());
auto defs = decompile_bitfield_from_int(type, ts, value);
std::vector<goos::Object> result;
result.push_back(pretty_print::to_symbol(fmt::format("new 'static '{}", type.print())));
for (auto& def : defs) {
if (def.is_signed) {
result.push_back(
pretty_print::to_symbol(fmt::format(":{} {}", def.field_name, (s64)def.value)));
} else {
result.push_back(
pretty_print::to_symbol(fmt::format(":{} #x{:x}", def.field_name, def.value)));
}
}
return pretty_print::build_list(result);
return bitfield_defs_print(type, defs);
}
std::vector<BitFieldConstantDef> decompile_bitfield_from_int(const TypeSpec& type,
@ -894,4 +915,17 @@ std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
return result;
}
std::string decompile_int_enum_from_int(const TypeSpec& type, const TypeSystem& ts, u64 value) {
auto type_info = ts.try_enum_lookup(type.base_type());
assert(type_info);
assert(!type_info->is_bitfield());
for (auto& field : type_info->entries()) {
if ((u64)field.second == value) {
return field.first;
}
}
throw std::runtime_error(
fmt::format("Failed to decompile integer enum. Value {} wasn't found in enum {}", value,
type_info->get_name()));
}
} // namespace decompiler

View file

@ -79,5 +79,6 @@ std::vector<BitFieldConstantDef> decompile_bitfield_from_int(const TypeSpec& typ
std::vector<std::string> decompile_bitfield_enum_from_int(const TypeSpec& type,
const TypeSystem& ts,
u64 value);
std::string decompile_int_enum_from_int(const TypeSpec& type, const TypeSystem& ts, u64 value);
} // namespace decompiler

View file

@ -136,4 +136,7 @@
- Fixed a bug where creating a stack array of 0 sized caused a compiler assertion. It is now a normal compiler error.
- Fixed a bug where the repl history was not loaded on compiler start
- Branch target addresses in the disassembly generated by `md`, `asm-file` with `:disassemble` and `(declare (print-asm))` are now correct
- Fixed a segfault when the `goal-library.gc` contains an `(exit)`
- Fixed a segfault when the `goal-library.gc` contains an `(exit)`
- `defenum` now creates real types. Boxed arrays of enums and bitfields correctly have runtime type of the parent integer.
- Added a `:copy-entries <typename>` to copy entries from a previous bitfield.
- Adding a duplicate entry to an enum now generates a compiler error.

View file

@ -6,12 +6,12 @@
;; dgos: GAME, ENGINE
(deftype vif-disasm-element (structure)
((mask uint32 :offset-assert 0)
(tag uint32 :offset-assert 4)
(val uint32 :offset-assert 8)
(print uint32 :offset-assert 12)
(string1 string :offset-assert 16)
(string2 string :offset-assert 20)
((mask uint32 :offset-assert 0)
(tag vif-cmd-32 :offset-assert 4)
(val uint32 :offset-assert 8)
(print uint32 :offset-assert 12)
(string1 string :offset-assert 16)
(string2 string :offset-assert 20)
)
:method-count-assert 9
:size-assert #x18
@ -55,182 +55,4 @@
(new 'static 'vif-disasm-element :mask #x6f :tag #x6f :val #x2 :print #x7 :string1 "unpack-v4-5")
(new 'static 'vif-disasm-element :print #x8)))
(defun disasm-vif-details ((stream symbol) (data (pointer uint8)) (kind int) (count int))
(local-vars
(v1-21 uint)
(v1-26 uint)
(a3-7 int)
(i int)
(s2-1 int)
(s2-2 int)
(s2-3 int)
(s2-4 int)
(s2-5 int)
(s2-6 int)
(data-ptr (pointer uint8))
(s3-1 (pointer uint8))
(s3-2 (pointer uint8))
(s3-3 (pointer uint8))
(s3-4 (pointer uint8))
(s3-5 (pointer uint8))
(s3-6 (pointer uint8))
(count2 int)
)
(set! count2 count)
(cond
((= kind 110)
(set! data-ptr (&-> data 4))
(set! i 0)
(while (< i count2)
(format stream " #x~X: #x~2X #x~2X #x~2X #x~2X~%"
(+ (+ (shl i 2) 4) (the-as int data))
(-> (the-as (pointer uint8) (&+ data-ptr (shl i 2))))
(-> (the-as (pointer uint8) (&+ data-ptr (+ (shl i 2) 1))))
(-> (the-as (pointer uint8) (&+ data-ptr (+ (shl i 2) 2))))
(-> (the-as (pointer uint8) (&+ data-ptr (+ (shl i 2) 3))))
)
(+! i 1)
)
)
((= kind 98)
(set! s3-1 (&-> data 4))
(set! s2-1 0)
(while (< s2-1 count2)
(format stream " #x~X: #x~2x~%" (+ (+ s2-1 4) (the-as int data)) count)
(set! v1-21 (-> (the-as (pointer uint8) (&+ s3-1 (* 3 s2-1)))))
(set! v1-26 (-> (the-as (pointer uint8) (&+ s3-1 (+ (* 3 s2-1) 1)))))
(+! s2-1 1)
)
)
((= kind 108)
(set! s3-2 (&-> data 4))
(set! s2-2 0)
(while (< s2-2 count2)
(format stream " #x~X: #x~8x #x~8x #x~8x #x~8x~%"
(+ (+ (shl s2-2 4) 4) (the-as int data))
(-> (the-as (pointer uint32) (&+ s3-2 (shl (shl s2-2 2) 2))))
(-> (the-as (pointer uint32) (&+ s3-2 (shl (+ (shl s2-2 2) 1) 2))))
(-> (the-as (pointer uint32) (&+ s3-2 (shl (+ (shl s2-2 2) 2) 2))))
(-> (the-as (pointer uint32) (&+ s3-2 (shl (+ (shl s2-2 2) 3) 2))))
)
(+! s2-2 1)
)
)
((= kind 109)
(set! s3-3 (&-> data 4))
(set! s2-3 0)
(while
(< s2-3 count2)
(format stream " #x~X: #x~4x #x~4x #x~4x #x~4x~%"
(+ (+ (shl s2-3 3) 4) (the-as int data))
(-> (the-as (pointer uint16) (&+ s3-3 (shl (shl s2-3 2) 1))))
(-> (the-as (pointer uint16) (&+ s3-3 (shl (+ (shl s2-3 2) 1) 1))))
(-> (the-as (pointer uint16) (&+ s3-3 (shl (+ (shl s2-3 2) 2) 1))))
(-> (the-as (pointer uint16) (&+ s3-3 (shl (+ (shl s2-3 2) 3) 1))))
)
(+! s2-3 1)
)
)
((= kind 104)
(set! s3-4 (&-> data 4))
(set! s2-4 0)
(while
(< s2-4 count2)
(format stream
" #x~X: #x~8x #x~8x #x~8x~%"
(+ (+ (* 12 s2-4) 4) (the-as int data))
(-> (the-as (pointer uint32) (&+ s3-4 (* 12 s2-4))))
(-> (the-as (pointer uint32) (&+ s3-4 (shl (+ (* 3 s2-4) 1) 2))))
(-> (the-as (pointer uint32) (&+ s3-4 (shl (+ (* 3 s2-4) 2) 2))))
)
(+! s2-4 1)
)
)
((= kind 105)
(set! s3-5 (&-> data 4))
(set! s2-5 0)
(while
(< s2-5 count2)
(format stream
" #x~X: #x~4x #x~4x #x~4x~%"
(+ (+ (* 6 s2-5) 4) (the-as int data))
(-> (the-as (pointer uint16) (&+ s3-5 (* 6 s2-5))))
(-> (the-as (pointer uint16) (&+ s3-5 (shl (+ (* 3 s2-5) 1) 1))))
(-> (the-as (pointer uint16) (&+ s3-5 (shl (+ (* 3 s2-5) 2) 1))))
)
(+! s2-5 1)
)
)
((= kind 101)
(set! s3-6 (&-> data 4))
(set! s2-6 0)
(while
(< s2-6 count2)
(format stream " #x~X: #x~4x #x~4x~%"
(+ (+ (shl s2-6 2) 4) (the-as int data))
(-> (the-as (pointer uint16) (&+ s3-6 (* 6 s2-6))))
(-> (the-as (pointer uint16) (&+ s3-6 (shl (+ (* 3 s2-6) 1) 1))))
)
(+! s2-6 1)
)
)
(else
(set! a3-7 kind)
(format stream
" #x~X: Data format #b~b not yet supported, add it for yourself!~%"
(&-> data 4)
a3-7
)
)
)
#f
)
(defun disasm-dma-tag ((arg0 uint) (arg1 symbol))
"Print out a dma-tag"
(local-vars
(v1-1 uint)
(v1-15 int)
)
(format arg1 "(dma-tag ")
;; this is a case used by value, which it doesn't like because the temp v1-1 can't be folded into a single
;; expression. In reality, the game does the following line _after_ the format symbol is loaded,
;; but this makes no difference and OpenGOAL doesn't have case, so we have to make do with this.
(set! v1-1 (shr (shl arg0 33) 61))
(format arg1 "~s"
(cond
((= v1-1 7) "end")
((= v1-1 6) "ret")
((= v1-1 5) "call")
((= v1-1 4) "refs")
((= v1-1 3) "ref")
((= v1-1 2) "next")
((= v1-1 1) "cnt")
((zero? v1-1) "refe")
(else "*unknown*")
)
)
;; only print address if nonzero
(if (> (shr (shl arg0 1) 33) 0)
(format arg1 " :addr #x~8x" (shr (shl arg0 1) 33))
)
;; only print qwc if nonzero
(if (> (shr (shl arg0 48) 48) 0)
(format arg1 " :qwc ~d" (shr (shl arg0 48) 48))
)
;; only print spr if set
(if (> (shr arg0 63) 0)
(format arg1 " :spr ~d" (shr arg0 63)))
;;(.srl v1-15 arg0 31)
(set! v1-15 (logand #xffffffff (shr arg0 31)))
(when (> (the-as uint v1-15) 0)
(format arg1 " :irq ~d" (logand #xffffffff (shr arg0 31)))
)
(if (> (shr (shl arg0 36) 62) 0)
(format arg1 " :pce ~d" (shr (shl arg0 36) 62))
)
(the-as int (format arg1 ")~%"))
)

View file

@ -169,34 +169,36 @@
:flag-assert #x900000008
)
(defenum dma-tag-id
:bitfield #f
:type uint8
(refe 0) ;; addr=ADDR, ends after this transfer
(cnt 1) ;; addr=after tag, next-tag=after data
(next 2) ;; addr=after tag, next-tag=ADDR
(ref 3) ;; addr=ADDR, next-tag=after tag
(refs 4) ;; ref, but stall controled
(call 5) ;;
(ret 6) ;;
(end 7) ;; next, but ends.
)
;; In source chain mode, the DMA controller reads "DMAtag"s to determine addresses
;; sizes, and the next thing to transfer.
;; A tag is 8 bytes.
(deftype dma-tag (uint64)
((qwc uint16 :offset 0) ;; quadword count
(pce uint8 :offset 26 :size 2) ;; priority (source mode)
(id uint8 :offset 28 :size 3) ;; ID (what the tag means)
(irq uint8 :offset 31 :size 1) ;; interrupt at the end?
(addr uint32 :offset 32 :size 31) ;; address (31 bits)
(spr uint8 :offset 63 :size 1) ;; spr or not flag.
((qwc uint16 :offset 0) ;; quadword count
(pce uint8 :offset 26 :size 2) ;; priority (source mode)
(id dma-tag-id :offset 28 :size 3) ;; ID (what the tag means)
(irq uint8 :offset 31 :size 1) ;; interrupt at the end?
(addr uint32 :offset 32 :size 31) ;; address (31 bits)
(spr uint8 :offset 63 :size 1) ;; spr or not flag.
)
:method-count-assert 9
:size-assert #x8
:flag-assert #x900000008
)
(defenum dma-tag-id
:bitfield #f
(refe 0) ;; addr=ADDR, ends after this transfer
(cnt 1) ;; addr=after tag, next-tag=after data
(next 2) ;; addr=after tag, next-tag=ADDR
(ref 3) ;; addr=ADDR, next-tag=after tag
(refs 4) ;; ref, but stall controled
(call 5) ;;
(ret 6) ;;
(end 7) ;; next, but ends.
)
;; A DMA bucket is a way of organizing data within a dma buffer.
;; The buckets themselves live inside in the dma buffer.
;; the addr field of their tag should point to the next bucket.
@ -252,38 +254,63 @@
)
;; all these have mask (only applies to unpacks) and interrupt not set.
(defenum vif-cmd
:bitfield #f
(nop 0) ;; no-op, can still have irq set.
(stcycl 1) ;; set write recycle register
(offset 2) ;; set offset register
(base 3) ;; set base register
(itop 4) ;; set data pointer register (itops)
(stmod 5) ;; set mode register
(mskpath3 6) ;; set path 3 mask
(mark 7) ;; set mark register
(flushe 16) ;; wait for end of microprogram
(flush 17) ;; wait for end of microprogram and transfer (path1/path2)
(flusha 19) ;; wait for end of microprogram and transfer (path1/path2/path3)
(mscal 20) ;; activate microprogram (call)
(mscalf 21) ;; flushe and activate (call)
(mscnt 23) ;; activate microprogram (continue)
(stmask 32) ;; set MASK register.
(strow 48) ;; set filling data
(strow 49) ;; set filling data
(mpg 74) ;; transfer microprogram
;; there's more...
:type uint8
(nop 0) ;; no-op, can still have irq set.
(stcycl 1) ;; set write recycle register
(offset 2) ;; set offset register
(base 3) ;; set base register
(itop 4) ;; set data pointer register (itops)
(stmod 5) ;; set mode register
(mskpath3 6) ;; set path 3 mask
(mark 7) ;; set mark register
(flushe 16) ;; wait for end of microprogram
(flush 17) ;; wait for end of microprogram and transfer (path1/path2)
(flusha 19) ;; wait for end of microprogram and transfer (path1/path2/path3)
(mscal 20) ;; activate microprogram (call)
(mscalf 21) ;; flushe and activate (call)
(mscnt 23) ;; activate microprogram (continue)
(stmask 32) ;; set MASK register.
(strow 48) ;; set filling data
(stcol 49) ;; set filling data
(mpg 74) ;; transfer microprogram
(direct 80) ;; straight to GIF.
(directhl 81)
(unpack-s-32 96)
(unpack-s-16 97)
(unpack-s-8 98)
;; 99 is invllid
(unpack-v2-32 100)
(unpack-v2-16 101)
(unpack-v2-8 102)
;; 103 is invalid
(unpack-v3-32 104)
(unpack-v3-16 105)
(unpack-v3-8 106)
;; 107 is invalid
(unpack-v4-32 108)
(unpack-v4-16 109)
(unpack-v4-8 110)
(unpack-v4-5 111)
)
;; this makes a copy of the above type, but uses a uint32.
(defenum vif-cmd-32
:bitfield #f
:type uint32
:copy-entries vif-cmd
)
;; The VIF also has tags to control it.
;; Different VIF commands (called VIFcode) have different tag layouts.
;; Different VIF commands (called VIFcode) have different tag layouts.;; dma-h
(deftype vif-tag (uint32)
((imm uint16 :offset 0 :size 16)
(num uint8 :offset 16 :size 8)
(cmd uint8 :offset 24 :size 7)
(irq uint8 :offset 31 :size 1)
(msk uint8 :offset 28 :size 1)
((imm uint16 :offset 0 :size 16)
(num uint8 :offset 16 :size 8)
(cmd vif-cmd :offset 24 :size 7)
(irq uint8 :offset 31 :size 1)
(msk uint8 :offset 28 :size 1)
)
:method-count-assert 9
:size-assert #x4

View file

@ -129,8 +129,70 @@
(busclk/256 2)
(hblank 3)
)
;; dma
(declare-type dma-buffer basic)
(defenum vif-cmd
:bitfield #f
:type uint8
(nop 0) ;; no-op, can still have irq set.
(stcycl 1) ;; set write recycle register
(offset 2) ;; set offset register
(base 3) ;; set base register
(itop 4) ;; set data pointer register (itops)
(stmod 5) ;; set mode register
(mskpath3 6) ;; set path 3 mask
(mark 7) ;; set mark register
(flushe 16) ;; wait for end of microprogram
(flush 17) ;; wait for end of microprogram and transfer (path1/path2)
(flusha 19) ;; wait for end of microprogram and transfer (path1/path2/path3)
(mscal 20) ;; activate microprogram (call)
(mscalf 21) ;; flushe and activate (call)
(mscnt 23) ;; activate microprogram (continue)
(stmask 32) ;; set MASK register.
(strow 48) ;; set filling data
(stcol 49) ;; set filling data
(mpg 74) ;; transfer microprogram
(direct 80) ;; straight to GIF.
(directhl 81)
(unpack-s-32 96)
(unpack-s-16 97)
(unpack-s-8 98)
;; 99 is invllid
(unpack-v2-32 100)
(unpack-v2-16 101)
(unpack-v2-8 102)
;; 103 is invalid
(unpack-v3-32 104)
(unpack-v3-16 105)
(unpack-v3-8 106)
;; 107 is invalid
(unpack-v4-32 108)
(unpack-v4-16 109)
(unpack-v4-8 110)
(unpack-v4-5 111)
)
(defenum vif-cmd-32
:bitfield #f
:type uint32
:copy-entries vif-cmd
)
(defenum dma-tag-id
:bitfield #f
:type uint8
(refe 0) ;; addr=ADDR, ends after this transfer
(cnt 1) ;; addr=after tag, next-tag=after data
(next 2) ;; addr=after tag, next-tag=ADDR
(ref 3) ;; addr=ADDR, next-tag=after tag
(refs 4) ;; ref, but stall controled
(call 5) ;;
(ret 6) ;;
(end 7) ;; next, but ends.
)
;; display-h
(deftype display-env (structure)
((pmode uint64 :offset-assert 0)

View file

@ -68,5 +68,3 @@
(let ((v0-3 0))
)
;; failed to figure out what this is:
(none)

View file

@ -142,5 +142,3 @@
;; definition for method 9 of type bounding-box
;; ERROR: function was not converted to expressions. Cannot decompile.
;; failed to figure out what this is:
(none)

View file

@ -45,5 +45,3 @@
(let ((v0-2 0))
)
;; failed to figure out what this is:
(none)

View file

@ -55,6 +55,5 @@
(let ((v0-0 0))
)
;; failed to figure out what this is:
(none)

View file

@ -138,7 +138,7 @@
(defun
dma-buffer-add-vu-function
((dma-buf dma-buffer) (vu-func vu-function) (arg2 int))
(let ((func-ptr (the-as pointer (&-> vu-func data 4)))
(let ((func-ptr (&-> vu-func data 4))
(qlen (-> vu-func qlength))
(origin (-> vu-func origin))
)
@ -204,8 +204,6 @@
(none)
)
;; failed to figure out what this is:
(none)

View file

@ -207,12 +207,12 @@
;; definition of type dma-tag
(deftype dma-tag (uint64)
((qwc uint16 :offset 0 :size 16)
(pce uint8 :offset 26 :size 2)
(id uint8 :offset 28 :size 3)
(irq uint8 :offset 31 :size 1)
(addr uint32 :offset 32 :size 31)
(spr uint8 :offset 63 :size 1)
((qwc uint16 :offset 0 :size 16)
(pce uint8 :offset 26 :size 2)
(id dma-tag-id :offset 28 :size 3)
(irq uint8 :offset 31 :size 1)
(addr uint32 :offset 32 :size 31)
(spr uint8 :offset 63 :size 1)
)
:method-count-assert 9
:size-assert #x8
@ -300,11 +300,11 @@
;; definition of type vif-tag
(deftype vif-tag (uint32)
((imm uint16 :offset 0 :size 16)
(num uint8 :offset 16 :size 8)
(cmd uint8 :offset 24 :size 7)
(irq uint8 :offset 31 :size 1)
(msk uint8 :offset 28 :size 1)
((imm uint16 :offset 0 :size 16)
(num uint8 :offset 16 :size 8)
(cmd vif-cmd :offset 24 :size 7)
(irq uint8 :offset 31 :size 1)
(msk uint8 :offset 28 :size 1)
)
:method-count-assert 9
:size-assert #x4
@ -334,8 +334,6 @@
;; definition for function dma-count-until-done
;; ERROR: function was not converted to expressions. Cannot decompile.
;; failed to figure out what this is:
(none)

View file

@ -411,8 +411,7 @@
;; failed to figure out what this is:
(dma-initialize)
;; failed to figure out what this is:
(none)

View file

@ -38,6 +38,5 @@
(let ((v0-1 0))
)
;; failed to figure out what this is:
(none)

View file

@ -508,8 +508,7 @@
arg0
)
;; failed to figure out what this is:
(none)

View file

@ -1292,5 +1292,3 @@
(let ((v0-3 0))
)
;; failed to figure out what this is:
(none)

View file

@ -57,6 +57,4 @@
(let ((v0-2 0))
)
;; failed to figure out what this is:
(none)

View file

@ -371,5 +371,4 @@
(let ((v0-11 0))
)
;; failed to figure out what this is:
(none)

View file

@ -2005,5 +2005,3 @@
(gp-5 a0-63 *active-pool*)
)
;; failed to figure out what this is:
(none)

View file

@ -788,8 +788,7 @@
(let ((v0-34 0))
)
;; failed to figure out what this is:
(none)

View file

@ -175,5 +175,3 @@
(the-as symbol #f)
)
;; failed to figure out what this is:
(none)

View file

@ -5,5 +5,3 @@
(let ((v0-0 0))
)
;; failed to figure out what this is:
(none)

View file

@ -707,5 +707,4 @@
;; definition for symbol *temp-string*, type string
(define *temp-string* (new 'global 'string 256 (the-as string #f)))
;; failed to figure out what this is:
(none)

View file

@ -230,6 +230,3 @@
(let ((v0-6 0))
)
;; failed to figure out what this is:
(none)

View file

@ -82,6 +82,4 @@
arg0
)
;; failed to figure out what this is:
(none)

View file

@ -1691,5 +1691,4 @@
;; definition for method 9 of type matrix
;; ERROR: function was not converted to expressions. Cannot decompile.
;; failed to figure out what this is:
(none)

View file

@ -38,5 +38,3 @@
(let ((v0-1 0))
)
;; failed to figure out what this is:
(none)

View file

@ -1171,9 +1171,3 @@
)
)
;; failed to figure out what this is:
(none)

View file

@ -234,9 +234,3 @@
(let ((v0-8 0))
)
;; failed to figure out what this is:
(none)

View file

@ -711,10 +711,3 @@
(-> obj value)
)
)
;; failed to figure out what this is:
(none)

View file

@ -163,10 +163,3 @@
;; failed to figure out what this is:
(let ((v0-7 0))
)
;; failed to figure out what this is:
(none)

View file

@ -174,6 +174,3 @@
)
)
;; failed to figure out what this is:
(none)

View file

@ -48,6 +48,3 @@
(let ((v0-2 0))
)
;; failed to figure out what this is:
(none)

View file

@ -108,9 +108,4 @@
(transform-matrix-calc! (the-as transform (-> tf trans)) dst-mat)
)
;; failed to figure out what this is:
(none)

View file

@ -4,7 +4,3 @@
;; failed to figure out what this is:
(let ((v0-0 0))
)
;; failed to figure out what this is:
(none)

View file

@ -20,6 +20,3 @@
;; failed to figure out what this is:
(let ((v0-2 0))
)
;; failed to figure out what this is:
(none)

View file

@ -964,6 +964,3 @@
;; definition for symbol *zero-vector*, type vector
(define *zero-vector* (new 'static 'vector))
;; failed to figure out what this is:
(none)

View file

@ -75,10 +75,3 @@
;; failed to figure out what this is:
(let ((v0-1 0))
)
;; failed to figure out what this is:
(none)

View file

@ -122,7 +122,4 @@
(let ((v0-4 0))
)
;; failed to figure out what this is:
(none)

View file

@ -4,6 +4,3 @@
;; failed to figure out what this is:
(let ((v0-0 0))
)
;; failed to figure out what this is:
(none)

View file

@ -113,9 +113,3 @@
(let ((v0-4 0))
)
;; failed to figure out what this is:
(none)

View file

@ -148,9 +148,9 @@ TEST_F(DataDecompTest, SimpleStructure) {
auto parsed = parse_data(input);
auto decomp = decompile_at_label(TypeSpec("vif-disasm-element"), parsed.label("L217"),
parsed.labels, {parsed.words}, dts->ts);
check_forms_equal(
decomp.print(),
"(new 'static 'vif-disasm-element :mask #x7f :tag #x1 :print #x2 :string1 \"stcycl\")");
check_forms_equal(decomp.print(),
"(new 'static 'vif-disasm-element :mask #x7f :tag (vif-cmd-32 stcycl) :print "
"#x2 :string1 \"stcycl\")");
}
TEST_F(DataDecompTest, VifDisasmArray) {
@ -209,13 +209,14 @@ TEST_F(DataDecompTest, VifDisasmArray) {
auto parsed = parse_data(input);
auto decomp =
decompile_at_label_guess_type(parsed.label("L148"), parsed.labels, {parsed.words}, dts->ts);
check_forms_equal(
decomp.print(),
"(new 'static 'boxed-array vif-disasm-element 3\n"
" (new 'static 'vif-disasm-element :mask #x7f :string1 \"nop\")\n"
" (new 'static 'vif-disasm-element :mask #x7f :tag #x1 :print #x2 :string1 \"stcycl\")\n"
" (new 'static 'vif-disasm-element :mask #x7f :tag #x2 :print #x1 :string1 \"offset\" "
" :string2 \"offset\"))");
check_forms_equal(decomp.print(),
"(new 'static 'boxed-array vif-disasm-element 3\n"
" (new 'static 'vif-disasm-element :mask #x7f :string1 \"nop\")\n"
" (new 'static 'vif-disasm-element :mask #x7f :tag (vif-cmd-32 stcycl) :print "
"#x2 :string1 \"stcycl\")\n"
" (new 'static 'vif-disasm-element :mask #x7f :tag (vif-cmd-32 offset) :print "
"#x1 :string1 \"offset\" "
" :string2 \"offset\"))");
}
TEST_F(DataDecompTest, ContinuePoint) {

View file

@ -952,7 +952,7 @@ TEST_F(FormRegressionTest, DmaBufferAddVuFunction) {
std::string type = "(function dma-buffer vu-function int symbol)";
std::string expected =
"(begin\n"
" (let ((v1-0 (the-as pointer (&-> arg1 data 4)))\n"
" (let ((v1-0 (&-> arg1 data 4))\n"
" (a3-0 (-> arg1 qlength))\n"
" (a1-1 (-> arg1 origin))\n"
" )\n"