matt-kempster / m2c

A MIPS and PowerPC decompiler.
GNU General Public License v3.0
405 stars 49 forks source link

Add EABI support #149

Open tge-was-taken opened 3 years ago

tge-was-taken commented 3 years ago

Argument registers are considered "unset" despite the given function prototype.

Context

void* dds3StartProcess(char *namestr, int priority, s16 starttimer, s16 shutdowntimer, void* processfunc, void* shutdownfunc, void* data);
s32 fun_100760(void* a1);
s32 fun_1009A0(void* a1);
s32 fun_2A8AF8(s32 a1);

Assembly (ee-gcc)

glabel dds3StartProcess
addiu $sp, $sp, -0x50
sd $s1, 0x8($sp)
move $s1, $a0
addiu $a0, $zero, 0x50
sd $s0, 0x0($sp)
sd $s2, 0x10($sp)
move $s2, $a1
sd $s3, 0x18($sp)
move $s3, $a2
sd $s4, 0x20($sp)
move $s4, $a3
sd $s5, 0x28($sp)
move $s5, $a4
sd $s6, 0x30($sp)
move $s6, $a5
sd $s7, 0x38($sp)
sd $ra, 0x40($sp)
jal fun_2a8af8
move $s7, $a6
move $s0, $v0
beqz $s0, .lbl_1016c0
move $a3, $zero
sw $zero, 0x18($s0)
lbu $v0, 0x0($s1)
sll $v1, $v0, 0x18
beqz $v1, .lbl_101660
sb $v0, 0x0($s0)
move $a2, $s1
lb $a0, 0x0($a2)
nop 
.lbl_101628:
addiu $a3, $a3, 0x1
lw $v1, 0x18($s0)
addu $v0, $s1, $a3
move $a2, $v0
addu $a1, $s0, $a3
addu $v1, $v1, $a0
slti $a0, $a3, 0x18
sw $v1, 0x18($s0)
lbu $v0, 0x0($a2)
sll $v1, $v0, 0x18
beqz $v1, .lbl_101660
sb $v0, 0x0($a1)
bnel $a0, $zero, .lbl_101628
lb $a0, 0x0($a2)
.lbl_101660:
addiu $v0, $zero, 0x1
sw $s2, 0x20($s0)
sw $v0, 0x1c($s0)
move $a0, $s0
sh $s3, 0x2c($s0)
sh $s4, 0x2e($s0)
sw $s5, 0x30($s0)
sw $s6, 0x34($s0)
sw $s7, 0x38($s0)
sb $zero, 0x17($s0)
sw $zero, 0x24($s0)
sw $zero, 0x28($s0)
sw $zero, 0x3c($s0)
sw $zero, 0x40($s0)
sw $zero, 0x44($s0)
sw $zero, 0x48($s0)
jal fun_100760
sw $zero, 0x4c($s0)
lh $v0, 0x2c($s0)
bnez $v0, .lbl_1016c0
move $v0, $s0
jal fun_1009a0
move $a0, $s0
move $v0, $s0
.lbl_1016c0:
ld $s0, 0x0($sp)
ld $s1, 0x8($sp)
ld $s2, 0x10($sp)
ld $s3, 0x18($sp)
ld $s4, 0x20($sp)
ld $s5, 0x28($sp)
ld $s6, 0x30($sp)
ld $s7, 0x38($sp)
ld $ra, 0x40($sp)
jr $ra
addiu $sp, $sp, 0x50
nop 

mips_to_c output

? fun_1009a0(void *); // extern
void *fun_2a8af8(?); // extern

void *dds3StartProcess(s8 *namestr, s32 priority, s16 starttimer, s16 shutdowntimer, void *processfunc, void *shutdownfunc, void *data) {
    s32 temp_a3;
    s8 *temp_v0_3;
    u8 temp_v0_2;
    u8 temp_v0_4;
    void *temp_ret;
    void *temp_v0;
    s8 phi_a0;
    s32 phi_a3;

    temp_ret = fun_2a8af8(0x50);
    temp_v0 = temp_ret;
    phi_a3 = 0;
    if (temp_v0 != 0) {
        temp_v0->unk18 = 0;
        temp_v0_2 = (u8) *namestr;
        temp_v0->unk0 = temp_v0_2;
        if ((temp_v0_2 << 0x18) != 0) {
            phi_a0 = *namestr;
loop_3:
            temp_a3 = phi_a3 + 1;
            temp_v0_3 = &namestr[temp_a3];
            temp_v0->unk18 = (s32) (temp_v0->unk18 + phi_a0);
            temp_v0_4 = (u8) *temp_v0_3;
            *(temp_v0 + temp_a3) = temp_v0_4;
            phi_a3 = temp_a3;
            if ((temp_v0_4 << 0x18) != 0) {
                if (temp_a3 < 0x18) {
                    phi_a0 = *temp_v0_3;
                    goto loop_3;
                }
            }
        }
        temp_v0->unk20 = priority;
        temp_v0->unk1C = 1;
        temp_v0->unk2C = starttimer;
        temp_v0->unk2E = shutdowntimer;
        temp_v0->unk30 = (s32) MIPS2C_ERROR(Read from unset register $a4);
        temp_v0->unk34 = (s32) MIPS2C_ERROR(Read from unset register $a5);
        temp_v0->unk38 = (s32) MIPS2C_ERROR(Read from unset register $a6);
        temp_v0->unk17 = 0;
        temp_v0->unk24 = 0;
        temp_v0->unk28 = 0;
        temp_v0->unk3C = 0;
        temp_v0->unk40 = 0;
        temp_v0->unk44 = 0;
        temp_v0->unk48 = 0;
        temp_v0->unk4C = 0;
        fun_100760(temp_v0);
        if (temp_v0->unk2C == 0) {
            fun_1009a0(temp_v0);
        }
    }
    return temp_ret;
}
tge-was-taken commented 3 years ago

To provide further context, this code uses the EABI64 ABI in single float mode, which is described as follows:

I8/I16 arguments promoted to I32
Integer arguments are passed in integer registers: $a0, $a1, $a2, $a3, $t0 ($a4), $t1 ($a5), $t2 ($a6), $t3 ($a7)
If single float mode, single FP arguments are passed in pairs within 32-bit mode: $f12, $f13, $f14, $f15, $f16, $f17, $f18, $f19
otherwise: $f12, $f14, $f16, $f18
If not single float mode, the first 4 double fp arguments are passed in single FP registers: $d6, $d7, $d8, $d9
I32, F32 values get stored in stack slots that are 4 bytes in size and 4-byte aligned
F64 values get stored in stack slots that are 8 bytes in size and 8-byte aligned
I32 are returned in registers: $v0, $v1
F32 are returned in registers: $f0, $f1
If not single float mode, F64 are returned in register: $d0