On this page
  1. Related
  2. Mechanics
  3. References
  4. Sources

Golden Sun is Camelot Software Planning’s 2001 role-playing game for the Game Boy Advance. The code read here is Coaltergeist’s goldensun-decomp, which builds a byte-identical North American and European ROM. Its battle damage, critical, and Psynergy formulas were still assembly at the commit read.

A lowered stat or a status such as Sleep can end early, on a roll that the unit’s Luck raises and the turns left lower1. The rolls come from a linear congruential generator with the C standard’s example constants, where Pokémon Emerald’s generator adds 246912.

Mechanics

MechanicKindHow
Status recoveryStat rolla lowered stat or a status ends at 0 turns, or earlier on a roll of about (3 × Luck − 5 × turns left + a base)% once 5 or fewer turns remain
Random number generatorFormulathe C standard's example constants, 1103515245 and 12345, with bits 8 to 23 as the result
Every claim about this game, by concept (2)
Random number generatorGolden Sun's RPGRandom is a linear congruential generator, x←1103515245x+12345(mod232) (0x41C64E6D and 0x3039; the state sRPGRNGState is a 32-bit unsigned int on the GBA), returning state >> 8 as an unsigned short: bits 8 to 23 of the state. RPGRandom2 scales one result to 0 to 99, as RPGRandom × 100 >> 16 (computed). These are the constants of the C standard's example rand, which returns bits 16 to 30 instead. Pokémon Emerald's Random keeps the multiplier but adds 24691 and returns the top 16 bits; pret's random.h defines a second step, ISO_RANDOMIZE2, with Golden Sun's 12345.Code
Status recoveryIn the matching C of the North American and European release, eight unnamed functions (Func_80bf250 to Func_80bf484) each count down one of a unit's turn counters by 1 per call: the attack, defense, and resist modifiers (attackModTurns, defenseModTurns, resistModTurns, field names from the decompilers' struct Unit), and delude, confuse, charm, stun, and sleep. A counter that reaches 0 ends the effect, and for a modifier also zeroes the modifier. Otherwise the unnamed Func_80bf208 rolls for an early end, with the turns left T and a base B per counter: 30 for an attack modifier, 20 for defense and resist, 30 for delude, 60 for confuse, 70 for charm, 40 for stun, 50 for sleep. A modifier rolls only while it is negative (a lowered stat), so a raised stat lasts its full count. The roll happens only when T is 5 or less, and succeeds when (3 × the unit's luck − 5T + B) × 655 is at least a 16-bit RPGRandom result, so a check succeeds about (3 × Luck − 5T + B)% of the time (computed: 655 is about 1% of 65,536; the comparison is signed, so a negative sum never succeeds, and a sum over 100 always does). Success zeroes the counter, and a modifier's value too. Func_80bf208 calls _GetUnit with no argument, so the unit it reads is whatever its first argument left in r0, the unit being checked (the atlas's reading of the matching build). Which routines call these eight, and how often, is still assembly.Code

References

  1. ^In the matching C of the North American and European release, eight unnamed functions (Func_80bf250 to Func_80bf484) each count down one of a unit's turn counters by 1 per call: the attack, defense, and resist modifiers (attackModTurns, defenseModTurns, resistModTurns, field names from the decompilers' struct Unit), and delude, confuse, charm, stun, and sleep. A counter that reaches 0 ends the effect, and for a modifier also zeroes the modifier. Otherwise the unnamed Func_80bf208 rolls for an early end, with the turns left T and a base B per counter: 30 for an attack modifier, 20 for defense and resist, 30 for delude, 60 for confuse, 70 for charm, 40 for stun, 50 for sleep. A modifier rolls only while it is negative (a lowered stat), so a raised stat lasts its full count. The roll happens only when T is 5 or less, and succeeds when (3 × the unit's luck − 5T + B) × 655 is at least a 16-bit RPGRandom result, so a check succeeds about (3 × Luck − 5T + B)% of the time (computed: 655 is about 1% of 65,536; the comparison is signed, so a negative sum never succeeds, and a sum over 100 always does). Success zeroes the counter, and a modifier's value too. Func_80bf208 calls _GetUnit with no argument, so the unit it reads is whatever its first argument left in r0, the unit being checked (the atlas's reading of the matching build). Which routines call these eight, and how often, is still assembly.CodeCoaltergeist/goldensun-decomp
    How we know
    416unsigned int Func_80bf208(int arg0, int arg1, int arg2)
    ⋮
    418	int v;
    419	int t;
    420	v = _GetUnit();
    421	if (arg1 <= 5) {
    422		int e;
    423		e = *(unsigned char *)(v + 0x42);
    424		t = (e * 3 - arg1 * 5 + arg2) * 0x28f;
    425		if (t >= (_RPGRandom() & 0xffff))
    426			return 1;
    ⋮
    428	return 0;
    
    src/battle/mechanics.c lines 416–429 at 2e5c504 · upstream ↗
    431unsigned int Func_80bf250(int unit) {
    ⋮
    438    base = _GetUnit(unit);
    439    p = base + 0x132;
    440    if (*p != 0) {
    441        c = --*p;
    442        zero = 0;
    443        if (c == 0) {
    444            base[0x133] = zero;
    445            return 1;
    ⋮
    447        q = (signed char *)(base + 0x133);
    448        if (*q < 0) {
    449            if (Func_80bf208(unit, *p, 0x1e) != 0) {
    ⋮
    
    src/battle/mechanics.c lines 431–457 at 2e5c504 · upstream ↗
    467    p = base + 0x134;
    ⋮
    475        q = (signed char *)(base + 0x135);
    476        if (*q < 0) {
    477            if (Func_80bf208(unit, *p, 0x14) != 0) {
    ⋮
    495    p = base + 0x136;
    ⋮
    503        q = (signed char *)(base + 0x137);
    504        if (*q < 0) {
    505            if (Func_80bf208(unit, *p, 0x14) != 0) {
    
    src/battle/mechanics.c lines 459–513 at 2e5c504 · upstream ↗
    518    p = _GetUnit(unit) + 0x138;
    519    if (*p != 0) {
    520        if (--*p == 0)
    521            return 1;
    522        if (Func_80bf208(unit, *p, 0x1e) != 0) {
    523            *p = 0;
    524            return 1;
    ⋮
    533    p = _GetUnit(unit) + 0x139;
    534    if (*p != 0) {
    535        if (--*p == 0)
    536            return 1;
    537        if (Func_80bf208(unit, *p, 0x3c) != 0) {
    ⋮
    
    src/battle/mechanics.c lines 515–587 at 2e5c504 · upstream ↗
    30    u8 luck;                   // 0x042
    
    include/rpg.h line 30 at 2e5c504 · upstream ↗
    50    u8 attackModTurns;         // 0x132
    51    u8 attackMod;              // 0x133
    52    u8 defenseModTurns;        // 0x134
    53    u8 defenseMod;             // 0x135
    54    u8 resistModTurns;         // 0x136
    55    u8 resistMod;              // 0x137
    56    u8 delude;                 // 0x138
    57    u8 confuse;                // 0x139
    58    u8 charm;                  // 0x13A
    59    u8 stun;                   // 0x13B
    60    u8 sleep;                  // 0x13C
    
    include/rpg.h lines 50–60 at 2e5c504 · upstream ↗
    62	.macro .export_func sym
    63	.thumb_stub _\sym, \sym
    64	.endm
    
    include/macros.inc lines 62–64 at 2e5c504 · upstream ↗
    58	.export_func RPGRandom
    
    exports/rpg/exports.s line 58 at 2e5c504 · upstream ↗
    5unsigned short RPGRandom(void) {
    ⋮
    8    return state >> 8;
    
    src/rpg/random.c lines 5–9 at 2e5c504 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-27.

    In the claim ledger

  2. ^Golden Sun's RPGRandom is a linear congruential generator, x←1103515245x+12345(mod232) (0x41C64E6D and 0x3039; the state sRPGRNGState is a 32-bit unsigned int on the GBA), returning state >> 8 as an unsigned short: bits 8 to 23 of the state. RPGRandom2 scales one result to 0 to 99, as RPGRandom × 100 >> 16 (computed). These are the constants of the C standard's example rand, which returns bits 16 to 30 instead. Pokémon Emerald's Random keeps the multiplier but adds 24691 and returns the top 16 bits; pret's random.h defines a second step, ISO_RANDOMIZE2, with Golden Sun's 12345.CodeCoaltergeist/goldensun-decomp · C99 draft N1256 · pret/pokeemerald
    How we know
    3extern unsigned int sRPGRNGState;
    ⋮
    5unsigned short RPGRandom(void) {
    6    unsigned int state = sRPGRNGState * 0x41c64e6d + 0x3039;
    7    sRPGRNGState = state;
    8    return state >> 8;
    ⋮
    12unsigned short RPGRandom2(void) {
    13    extern int RPGRandom_i(void) __asm__("RPGRandom");
    14    return RPGRandom_i() * 0x64 >> 16;
    
    src/rpg/random.c lines 3–15 at 2e5c504 · upstream ↗
    next = next * 1103515245 + 12345; return (unsigned int)(next/65536) % 32768;
    §7.20.2.2 ¶5, p. 312 · archived copy ↗
    11u16 Random(void)
    ⋮
    13    gRngValue = ISO_RANDOMIZE1(gRngValue);
    14    sRandCount++;
    15    return gRngValue >> 16;
    
    src/random.c lines 11–16 at 731ad5b · upstream ↗
    14// The number 1103515245 comes from the example implementation of rand and srand
    15// in the ISO C standard.
    16#define ISO_RANDOMIZE1(val) (1103515245 * (val) + 24691)
    17#define ISO_RANDOMIZE2(val) (1103515245 * (val) + 12345)
    
    include/random.h lines 14–17 at 731ad5b · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-27.

    In the claim ledger

Sources

Code

Primary

  • JTC1/SC22/WG14, ISO/IEC. 2007. “ISO/IEC 9899:TC3, Committee Draft WG14/N1256 (C99 with Technical Corrigenda 1–3).” ISO/IEC JTC1/SC22/WG14. original · archived · licence, all uses