summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryenatch <yenatch@gmail.com>2017-05-27 17:13:48 -0400
committeryenatch <yenatch@gmail.com>2017-05-27 17:18:20 -0400
commit06218139dc0b7e015850ac9dd5d8658023339a0c (patch)
tree2b0414e19891b3776adab65a9ef0c47e0248ee67
parent66e51b641b3ff914ecdaf6a10e08135b4abd9945 (diff)
Add CONTRIBUTING.md
-rw-r--r--CONTRIBUTING.md340
1 files changed, 340 insertions, 0 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..15ff9dfb3
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,340 @@
+# Decompiling
+
+Code starts out in `asm/`. When decompiled to C, it goes into `src/`. The goal is to decompile all the code.
+
+Some of the code in `asm/` is handwritten assembly. It can't and shouldn't be decompiled. It's already commented, so there's no further work to do on these files.
+* `asm/crt0.s`
+* `asm/libagbsyscall.s`
+* `asm/libgcnmultiboot.s`
+* `asm/m4a_1.s`
+* `asm/m4a_3.s`
+
+The rest of the `.s` files in `asm/` are fair game.
+
+The basic decompilation process is:
+* Choose a file in `asm/`, i.e. `asm/x.s`. Create a C file called `src/x.c`.
+* Translate the first function in `asm/x.s` to C in `src/x.c`.
+* `make compare`, and tweak the function until it matches.
+* Clean up the code and comment.
+* Repeat for each function until `asm/x.s` is empty.
+
+
+# For example, let's decompile `asm/cable_car.s`.
+
+
+## 1. Create `src/cable_car.c`
+
+```c
+#include "global.h"
+```
+
+`global.h` contains typedefs for GBA programming and more.
+It must be the first include in the file. Other includes will assume you have included it.
+
+
+## 2. Include it in the rom
+
+Include `src/cable_car.c` in the rom by adding `src/cable_car.c` to `ld_script.txt`:
+```diff
+ asm/battle_message.o(.text);
+ asm/choose_party.o(.text);
++ src/cable_car.o(.text);
+ asm/cable_car.o(.text);
+ asm/roulette_util.o(.text);
+```
+Do not remove `asm/cable_car.o(.text)`. We want both `src/cable_car.c` and `asm/cable_car.s` in the rom.
+
+
+## 3. Translate the function to C
+
+Take the first function in `asm/cable_car.s`. Either comment it out or remove it, whichever is easier.
+
+```asm
+ thumb_func_start sub_81231EC
+sub_81231EC: @ 81231EC
+ push {r4,lr}
+ lsls r0, 24
+ lsrs r4, r0, 24
+ ldr r0, _08123210 @ =gPaletteFade
+ ldrb r1, [r0, 0x7]
+ movs r0, 0x80
+ ands r0, r1
+ cmp r0, 0
+ bne _0812320A
+ ldr r0, _08123214 @ =sub_8123244
+ bl SetMainCallback2
+ adds r0, r4, 0
+ bl DestroyTask
+_0812320A:
+ pop {r4}
+ pop {r0}
+ bx r0
+ .align 2, 0
+_08123210: .4byte gPaletteFade
+_08123214: .4byte sub_8123244
+ thumb_func_end sub_81231EC
+```
+---
+
+Then, start translating the code to `src/cable_car.c`, bit by bit:
+
+```asm
+ lsls r0, 24
+ lsrs r4, r0, 24
+```
+```c
+void sub_81231EC(u8 r4) {
+```
+---
+```asm
+ ldr r0, _08123210 @ =gPaletteFade
+ ldrb r1, [r0, 0x7]
+ movs r0, 0x80
+ ands r0, r1
+```
+```c
+ r0 = (u8 *)(&gPaletteFade + 7) & 0x80;
+```
+---
+
+---
+```asm
+ cmp r0, 0
+ bne _0812320A
+```
+```c
+ if (!r0) {
+```
+---
+```asm
+ ldr r0, _08123214 @ =sub_8123244
+ bl SetMainCallback2
+```
+```c
+ SetMainCallback2(&sub_8123244);
+```
+---
+```asm
+ adds r0, r4, 0
+ bl DestroyTask
+```
+```c
+ DestroyTask(r4);
+```
+---
+```asm
+_0812320A:
+```
+```c
+ }
+```
+---
+```asm
+ pop {r4}
+ pop {r0}
+ bx r0
+```
+```c
+ return;
+```
+The type signature of the function depends on the return type.
+* `bx r0`: `void`
+* `bx r1`: `*`
+* `bx lr`: `void`, `*`
+
+You will need to look at the caller and the function prologue to determine the exact type if not void.
+
+Since it used `bx r0`, it's `void` for sure.
+
+---
+
+Putting it all together, we get:
+```c
+void sub_81231EC(u8 r4) {
+ r0 = (u8 *)(&gPaletteFade + 7) & 0x80;
+ if (!r0) {
+ SetMainCallback2(&sub_8123244);
+ DestroyTask(r4);
+ }
+ return;
+}
+```
+
+
+## 4. Simplify and document
+
+This line doesn't look quite right.
+
+```c
+ r0 = (u8 *)(&gPaletteFade + 7) & 0x80;
+```
+
+What is `gPaletteFade`? You can find out where stuff is with `git grep`:
+
+```sh
+git grep "gPaletteFade" include/
+```
+```grep
+include/palette.h:extern struct PaletteFadeControl gPaletteFade;
+```
+
+So it's a struct called `PaletteFadeControl`. Let's look in `palette.h`:
+
+```c
+struct PaletteFadeControl
+{
+ u32 multipurpose1;
+ u8 delayCounter:6;
+ u16 y:5; // blend coefficient
+ u16 targetY:5; // target blend coefficient
+ u16 blendColor:15;
+ u16 active:1;
+ u16 multipurpose2:6;
+ u16 yDec:1; // whether blend coefficient is decreasing
+ u16 bufferTransferDisabled:1;
+ u16 mode:2;
+ u16 shouldResetBlendRegisters:1;
+ u16 hardwareFadeFinishing:1;
+ u16 softwareFadeFinishingCounter:5;
+ u16 softwareFadeFinishing:1;
+ u16 objPaletteToggle:1;
+ u8 deltaY:4; // rate of change of blend coefficient
+};
+```
+---
+
+What's the 7th byte in this struct?
+```c
+ u32 multipurpose1; // 0-3
+ u8 delayCounter:6; // 4
+ u16 y:5; // 5
+ u16 targetY:5; // 5-6
+ u16 blendColor:15; // 7
+ u16 active:1; // 7
+```
+
+Byte 7 has both `.blendColor` and `.active`.
+
+---
+
+Okay, what's 0x80 mean? It's `0b10000000`, which is the highest bit in a byte.
+
+`.active` comes after, which means it's higher, but it's also only one bit, so it's a safe bet.
+
+```c
+ r0 = gPaletteFade.active;
+```
+
+Much better.
+
+---
+
+```c
+void sub_81231EC(u8 r4) {
+ r0 = gPaletteFade.active;
+ if (!r0) {
+ SetMainCallback2(&sub_8123244);
+ DestroyTask(r4);
+ }
+ return;
+}
+```
+
+Now the temp variable `r0` is a little pointless. We can simplify this to:
+
+```c
+void sub_81231EC(u8 taskId) {
+ if (!gPaletteFade.active) {
+ SetMainCallback2(&sub_8123244);
+ DestroyTask(taskId);
+ }
+}
+```
+
+Looks done, right?
+This function is pretty simple, so it doesn't need any comments right now.
+
+But what about `sub_8123244`? It's still not obvious what that function does. We can find out by decompiling it later.
+
+
+## 5. Build
+
+```sh
+make compare
+```
+```gcc
+src/cable_car.c: In function `sub_81231EC':
+src/cable_car.c:4: `gPaletteFade' undeclared (first use in this function)
+src/cable_car.c:4: (Each undeclared identifier is reported only once for each function it appears in.)
+src/cable_car.c:5: warning: implicit declaration of function `SetMainCallback2'
+src/cable_car.c:5: `sub_8123244' undeclared (first use in this function)
+src/cable_car.c:6: warning: implicit declaration of function `DestroyTask'
+```
+
+We got some errors. We need to tell the compiler what `gPaletteFade`, `SetMainCallback2`, `sub_8123244`, and `DestroyTask` are.
+
+We know `gPaletteFade` is from `palette.h`. We can do the same with the others. Declare them above the function:
+```c
+#include "palette.h"
+#include "main.h"
+#include "task.h"
+```
+The odd one out is `sub_8123244`, which is in `asm/cable_car.s`! What then?
+```c
+void sub_8123244();
+```
+Normally, we would do `extern void sub_8123244();`, but it won't be `extern` when we're done this file.
+
+---
+
+Now our file looks like this:
+```c
+#include "global.h"
+#include "palette.h"
+#include "main.h"
+#include "task.h"
+
+void sub_8123244();
+
+void sub_81231EC(u8 taskId) {
+ if (!gPaletteFade.active) {
+ SetMainCallback2(&sub_8123244);
+ DestroyTask(taskId);
+ }
+}
+```
+
+---
+
+Build again, and we get:
+```sh
+make compare
+```
+```sha1sum
+pokeruby.gba: OK
+```
+
+This means the function matches. Congratulations!
+
+---
+
+If it doesn't match, you will get:
+```sha1sum
+pokeruby.gba: FAILED
+sha1sum: WARNING: 1 computed checksum did NOT match
+```
+
+---
+
+If you forgot to remove the function from `asm/cable_car.s`, you will get this error:
+```gcc
+asm/cable_car.o: In function `sub_81231EC':
+(.text+0x0): multiple definition of `sub_81231EC'
+src/cable_car.o:(.text+0x0): first defined here
+```
+
+
+## 6. Repeat until `asm/cable_car.s` is empty
+
+Once you're done, you can delete `asm/cable_car.s`, and remove it from `ld_script.txt`.
+