pokeruby/CONTRIBUTING.md

341 lines
6.9 KiB
Markdown
Raw Normal View History

2017-05-27 21:13:48 +00:00
# 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`.