Skip to content

Latest commit

 

History

History
218 lines (172 loc) · 4.29 KB

reference_implementation.md

File metadata and controls

218 lines (172 loc) · 4.29 KB

Reference implementation License Badge

This whole file is licensed under CC0-1.0. See docs/LICENSE.

This file contains a reference implementation of the IPL3 checksum algorithm, adapted to work with every known retail CIC/IPL3 variant.

This implementation is a very crude direct translation from the original assembly and it could be greatly simplified in a reimplementation.

/* SPDX-License-Identifier: CC0-1.0 */

#include <stdint.h>

uint32_t readWord(const uint8_t *rom , uint32_t offset) {
    return (rom[offset] << 24) | (rom[offset+1] << 16) | (rom[offset+2] << 8) | (rom[offset+3]);
}

/**
 * This value is passed by the PIF ROM to IPL3 via the $s6 register.
 */
uint32_t getSeed(uint32_t cic) {
    switch (cic) {
        case 6101:
        case 6102:
        case 7101:
        case 7102:
        default:
            return 0x3F;

        case 6103:
        case 7103:
            return 0x78;

        case 6105:
        case 7105:
            return 0x91;

        case 6106:
        case 7106:
            return 0x85;
    }
}

/**
 * This value is hardcoded into the IPL3.
 */
uint32_t getMagic(uint32_t cic) {
    switch (cic) {
        case 6101:
        case 6102:
        case 7101:
        case 7102:
        case 6105:
        case 7105:
        default:
            return 0x5D588B65;

        case 6103:
        case 7103:
        case 6106:
        case 7106:
            return 0x6C078965;
    }
}

void calculateChecksum(const uint8_t *rom, uint32_t cic, uint32_t *dst1, uint32_t *dst2) {
    uint32_t seed = getSeed(cic);
    uint32_t magic = getMagic(cic);
    uint32_t at;
    uint32_t v0, v1;
    uint32_t a0, a1, a2, a3;
    uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
    uint32_t s0, s6, ra, lo;
    uint32_t entrypointRam;

    s6 = seed;

    a0 = readWord(rom, 8);
    switch (cic) {
        case 6103:
        case 7103:
            a0 -= 0x100000;
            break;

        case 6106:
        case 7106:
            a0 -= 0x200000;
            break;
    }

    /* Tweak: The entrypoint is kept to be able to adjust some rom reads */
    entrypointRam = a0;

    at = magic;
    lo = s6 * at;

    switch (cic) {
        case 6105:
        case 7105:
            s6 = 0xA0000200;
            break;
    }

    ra = 0x100000;

    v1 = 0;
    t0 = 0;
    t1 = a0;

    t5 = 0x20;

    v0 = lo;
    v0 += 1;

    a3 = v0;
    t2 = v0;
    t3 = v0;
    s0 = v0;
    a2 = v0;
    t4 = v0;

    do {
        /* lw $v0, 0x0($t1) */
        v0 = readWord(rom, t1 - entrypointRam + 0x1000);
        v1 = a3 + v0;

        at = v1 < a3;

        a1 = v1;

        if (at != 0) {
            t2 = t2 + 0x1;
        }

        v1 = v0 & 0x1F;
        t7 = t5 - v1;

        t8 = v0 >> t7;
        t6 = v0 << v1;

        a0 = t6 | t8;
        at = a2 < v0;
        a3 = a1;

        t3 = t3 ^ v0;

        s0 = s0 + a0;

        if (at != 0) {
            t9 = a3 ^ v0;
            a2 = t9 ^ a2;
        } else {
            a2 = a2 ^ a0;
        }

        switch (cic) {
            case 6105:
            case 7105:
                /* ipl3 6105 copies 0x330 bytes from the ROM's offset 0x000554 (or offset 0x000514 into IPL3) */
                /* to vram 0xA0000004 */
                /* lw $t7, 0x0($s6) */
                t7 = readWord(rom, s6 - 0xA0000004 + 0x000554);

                t0 = t0 + 0x4;
                s6 = s6 + 0x4;
                t7 = v0 ^ t7;

                t4 = t7 + t4;

                t7 = 0xA00002FF;

                t1 = t1 + 0x4;

                s6 = s6 & t7;
                break;

            default:
                t0 = t0 + 0x4;
                t7 = v0 ^ s0;
                t1 = t1 + 0x4;
                t4 = t7 + t4;
                break;
        }
    } while (t0 != ra);

    switch (cic) {
        case 6103:
        case 7103:
            t6 = a3 ^ t2;
            a3 = t6 + t3;
            t8 = s0 ^ a2;
            s0 = t8 + t4;
            break;

        case 6106:
        case 7106:
            t6 = a3 * t2;
            a3 = t6 + t3;
            t8 = s0 * a2;
            s0 = t8 + t4;
            break;

        default:
            t6 = a3 ^ t2;
            a3 = t6 ^ t3;
            t8 = s0 ^ a2;
            s0 = t8 ^ t4;
            break;
    }

    *dst1 = a3;
    *dst2 = s0;
}