From c77ae06d150225ed2a82cb61c9b0bcb4b4586fe2 Mon Sep 17 00:00:00 2001 From: Rozelette Date: Sun, 19 Apr 2020 00:09:06 -0500 Subject: [PATCH] Created Decompiling Your First Function (markdown) --- Decompiling-Your-First-Function.md | 84 ++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 Decompiling-Your-First-Function.md diff --git a/Decompiling-Your-First-Function.md b/Decompiling-Your-First-Function.md new file mode 100644 index 0000000..31f5de2 --- /dev/null +++ b/Decompiling-Your-First-Function.md @@ -0,0 +1,84 @@ +Let's take the following function from z_actor and convert it to C. + +``` +glabel Actor_SetChestFlag +/* 016500 0x800B5C90 8C8E1E68 */ lw $t6, 0X1E68($a0) +/* 016501 0x800B5C94 240F0001 */ li $t7, 1 +/* 016502 0x800B5C98 00AFC004 */ sllv $t8, $t7, $a1 +/* 016503 0x800B5C9C 01D8C825 */ or $t9, $t6, $t8 +/* 016504 0x800B5CA0 AC991E68 */ sw $t9, 0X1E68($a0) +/* 016505 0x800B5CA4 03E00008 */ jr $ra +/* 016506 0x800B5CA8 00000000 */ nop +``` + +Let's take a look at the first line. + +``` +/* 016500 0x800B5C90 8C8E1E68 */ lw $t6, 0X1E68($a0) +``` + +There are two things that we can take away from this line. + +First, `$a0` is in use. `$a0` contains the first argument to the function, and thus we know that this function has at least one argument. + +Second, we know that `$a0` is a pointer because it is loading something relative to the address it points to and storing it in `$t6`. Since the `lw` or "load word" instruction is being used, we know its loading a 32-bit integer. + +``` +void Actor_SetChestFlag(u32 a0) { + u32 t6 = *(u32*)(a0 + 0x1E68); + +} +``` + +Let's take a look at the next few lines: + +``` +/* 016501 0x800B5C94 240F0001 */ li $t7, 1 +/* 016502 0x800B5C98 00AFC004 */ sllv $t8, $t7, $a1 +/* 016503 0x800B5C9C 01D8C825 */ or $t9, $t6, $t8 +``` + +An integer value 1 is being loaded into register `$t7`. This value is then shifted to the left using `$a1` to determine how many bits it should be shifted, with the result being saved in $t8. Finally, this result is Binary OR'd with the contents of `$t6`. This roughly translates to: + +``` +void Actor_SetChestFlag(u32 a0, u32 a1) { + u32 t6 = *(u32*)(a0 + 0x1E68); + u32 t7 = 1; + u32 t8 = t7 << a1; + u32 t9 = t6 | t8; + +} +``` + +Let's take a look at the final three lines: + +``` +/* 016504 0x800B5CA0 AC991E68 */ sw $t9, 0X1E68($a0) +/* 016505 0x800B5CA4 03E00008 */ jr $ra +/* 016506 0x800B5CA8 00000000 */ nop +``` + +The result of these calculations are saved back into `$a0 + 0x1E68`, then the function returns. The `nop` after the `jr` is necessary because the instruction in the binary after a jump is executed first due to the use of a delay slot. + +This translates to: + +``` +void Actor_SetChestFlag(u32 a0, u32 a1) { + u32 t6 = *(u32*)(a0 + 0x1E68); + u32 t7 = 1; + u32 t8 = t7 << a1; + u32 t9 = t6 | t8; + + *(u32*)(a0 + 0x1E68) = t9; +} +``` + +This can be simplified down to: + +``` +void Actor_SetChestFlag(u32 a0, u32 a1) { + *(u32*)(a0 + 0x1E68) |= (1 << a1); +} +``` + +Be sure to comment out the `#pragma GLOBAL_ASM` line with your function. Otherwise, you'll get an error about duplicate functions. \ No newline at end of file