/* SM64 Level Script Command Notes and Decompilation queueRAM, 18 July 2015 0x03 (8037E47C): delay frames 03 04 [XX XX] X = delay frames sets s16 flag (8038BE20) to 0 before delay and 1 after does not increment level script pointer until after delay 0x04 (8037E4FC): delay frames and signal end 04 04 [XX XX] X = delay frames identical to 0x03, except for flag set at start: sets s16 flag (8038BE20) to -1 before delay and 1 after does not increment level pointer until after delay 0x05 (8037E580): jump to level script at segmented address 05 08 00 00 [XX XX XX XX] X = segmented address of jump 0x06 (8037E580): push script stack and jump to level script at segmented address 06 08 00 00 [XX XX XX XX] X = segmented address of jump similar to 0x05, but also pushes current place on script stack so it can be popped later 0x07 (8037E620): pop script stack, returns to where previous 0x06 or 0x0C pushed from 07 04 00 00 0x08 (8037E620): push script stack and 16-bit value on stack 08 04 [XX XX] X = 16 bit value pushed on script stack after current address this command is never used in any of the level scripts 0x09 (8037E6D4): pops script stack and param (possibly from previous 0x08 or 0x0A) 09 04 00 00 this command is never used in any of the level scripts 0x0A (8037E780): pushes next level command on script stack and param 0x00000000 0A 04 00 00 this command is only used once in main level scripts 0x0B (8037E7F8): if result of operation is true, pop stack 0B 08 [XX] 00 [YY YY YY YY] X = operation, a0 to 8037E1A0 (unsigned 8) 0: level_accum & argument 1: !(level_accum & argument) 2: level_accum == argument 3: level_accum != argument 4: level_accum < argument 5: level_accum <= argument 6: level_accum > argument 7: level_accum >= argument Y = argument, a1 to 8037E1A0 (signed 32) this command is only used once in main level scripts 0x0C (8037E878): if result of operation is true, jumps to segmented address 0C 0C [XX] 00 [YY YY YY YY] [ZZ ZZ ZZ ZZ] X = operation, a0 to 8037E1A0 (unsigned 8). see 0x0B for valid operation values Y = argument, a1 to 8037E1A0 (signed 32) Z = segment address of jump 0x0D (8037E8E8): if result of operation is true, pushes next command on stack and jumps to segmented address 0D 0C [XX] 00 [YY YY YY YY] [ZZ ZZ ZZ ZZ] X = operation, a0 to 8037E1A0 (unsigned 8). see 0x0B for valid operation values Y = argument, a1 to 8037E1A0 (signed 32) Z = segment address of jump 0x0E (8037E988): if result of operation is false, skips over following 0xF, 0x10 commands 0E 08 [XX] 00 [YY YY YY YY] X = operation, a0 to 8037E1A0 (unsigned 8). see 0x0B for valid operation values Y = argument, a1 to 8037E1A0 (signed 32) this command is never used in any of the level scripts 0x0F (8037EA18): skips over the following 0x10 commands 0F 04 00 00 this command is never used in any of the level scripts 0x10 (8037EA70): doesn't do anything, nop 10 04 00 00 this command is never used in any of the level scripts 0x11 (8037EA98): updates accumulator from custom assembly 11 08 [XX XX] [YY YY YY YY] X = argument passed to assembly routine Y = assembly routine level_accum = Y(X, level_accum); 0x16 (8037EC54): copy raw data from ROM to RAM address and call 0x802786F0 16 10 00 00 [RR RR RR RR] [BB BB BB BB] [EE EE EE EE] R = RAM virtual address B = beginning offset in ROM E = ending offset in ROM 0x17 (8037ECA4): copy raw data from ROM to RAM segment 17 0C [SS SS] [BB BB BB BB] [EE EE EE EE] S = RAM segment number B = beginning offset in ROM E = ending offset in ROM 0x18 (8037ECF8): decompress MIO0 data from ROM to RAM segment 18 0C [SS SS] [BB BB BB BB] [EE EE EE EE] S = RAM segment number B = beginning offset in ROM E = ending offset in ROM 0x21 (8037F164): load polygon data without a geometry layout 21 08 [X0] [YY] [ZZ ZZ ZZ ZZ] X = some value stored in upper 8 bits of node Y = ID of the model to be used with 3D objects Z = segment offset address Only used in main level scripts: 21 08 4 0 A4, 04032A18 - 16x16 @ 04032780 - some white ball 21 08 4 0 9F, 0302BCD0 - 16x16 @ 0302BAD0 - some weird orange blob 21 08 4 0 A1, 0301CB00 - 32x32 @ 0301C300 - some weird white blob 21 08 4 0 9E, 0302C8A0 - 16x16 @ 0302C6A0 - white snow ball? 21 08 4 0 84, 08025F08 - 16x32 @ 08025A80 - white exclamation mark 21 08 1 0 DA, 08024BB8 - 32x64 @ 08023998 - pushable box 21 08 1 0 C9, 080048E0 - 32x32 @ 08004058 - cannon door 21 08 5 0 57, 05013CB8 - loaded with 0x15000788 in water levels - segment 05 is sea creatures - 32x64 ia16 @ 05012848 - wave pattern 21 08 6 0 54, 05000840 - loaded with 0x150007E8 in HMC, TTM - segment 05 is monkey/mole hole - 32x32 ia16 @ 05000040 - mole hole 21 08 4 0 54, 05002E00 - loaed with 0x15000830 in cap switch levels and slide - segment 05 is checkboard question mark - 32x64 ia16 @ 05001C48 - exclamtion mark 21 08 1 0 56, 05003120 - loaed with 0x15000830 in cap switch levels and slide - segment 05 is checkboard question mark - 16x4 @ 05002C48 - some gold gradient thingy 0x34 (8037FDE4): blank or unblank screen (call osViBlank(bool)) 34 04 [XX] 00 X = argument passed to osViBlank (0 = unblank, 1 = blank) */ // types typedef unsigned char u8; typedef unsigned short u16; typedef signed short s16; typedef unsigned long u32; typedef signed long s32; // data void **scriptStack; // 0x8038B8B0 void **pushStack; // 0x8038B8B4 u8 *level_ptr; // pointer to current place in level buffer at 0x8038BE28 u32 level_accum; // accumulator used by 0B, 0C, 0D, 0E, 11, 13, stored at 0x8038BE24 s16 some_flag; // flag used by level scripts 03, 04, 12, and top level function at 0x8038BE20 u16 level03_delay; // delay for level script 03 at 0x8038B8A4 u16 level04_delay; // delay for level script 04 at 0x8038B8A8 void *geo_layout_table[0x100]; // at 0x8032ddc4 // ---------------------------------------------------------------------------- // helper functions // ---------------------------------------------------------------------------- // pool allocation typedef struct { u8 *prev; u8 *next; } pool_t; // 0x8033B400: array of the base virtual addresses for each segment u32 segment_bases[32]; u32 pool_avail; // 0x8033B480 u8 *pool_begin; // 0x8033B484 u8 *pool_end; // 0x8033B488 pool_t *pool; // 0x8033B48C pool_t *pool_tail; // 0x8033B490 // called with InitMemPool(0x8005C000, 0x801C1000) void InitMemPool(u32 begin, u32 end) // 80278074/033074 { pool_begin = ((begin + 0xF) & 0xFFFFFFF0) + 0x10; pool_end = (end & 0xFFFFFFF0) - 0x10; pool_avail = pool_end - pool_begin; pool = pool_begin - 0x10; pool_tail = pool_end; pool->prev = NULL; pool->next = NULL; pool_tail->prev = NULL; pool_tail->next = NULL; } u8 *_pool_alloc(u32 length, u32 tail) // 80278120/033120 { u8 *alloc = NULL; // 0x00(sp) pool_t *next_pool; // 0x04(sp) length = (length + 0xF) & 0xFFFFFFF0; length += 0x10; if (length != 0) { if (length <= pool_avail) { pool_avail -= length; if (tail == 0) { next_pool = pool + length; pool->next = next_pool; next_pool->prev = pool; next_pool->next = NULL; alloc = pool + 0x10; pool = next_pool; } else { next_pool = pool_tail - length; pool_tail->prev = next_pool; next_pool->next = pool_tail; next_pool->prev = NULL; pool_tail = next_pool; alloc = pool_tail + 0x10; } } } return alloc; } // free pool allocation u32 _pool_free(u8 *alloc) { pool_t *node04; // 0x04(sp) pool_t *node00; // 0x00(sp) node04 = (pool_t*)(alloc - 0x10); node00 = (pool_t*)(alloc - 0x10); if (node00 < pool) { while (node00->next != NULL) { node00 = node00->next; } pool = node04; pool->next = NULL; length = node00 - pool; pool_avail += length; } else { while (node00->prev != NULL) { node00 = node00->prev; } pool_tail = node04->next; pool_tail->prev = NULL; length = pool_tail - node00; pool_avail += length; } return pool_avail; } u32 SetSegmentBase(u32 segment, u32 virt_addr) // 80277EE0/032EE0 { segment_bases[segment] = virt_addr & 0x1FFFFFFF; return segment_bases[segment]; } u32 DynamicCopy(u32 rom_start, u32 rom_end, u32 tail) // 80278610/033610 { u32 length = ((rom_end - rom_start) + 0xF) & 0xFFFFFFF0; u32 alloc = _pool_alloc(length, tail); if (alloc != 0) { DmaCopy(alloc, rom_start, rom_end); } return alloc; } u32 DynamicIndexCopy(u32 segment, u32 rom_start, u32 rom_end, u32 tail) // 8027868C/03368C { u32 virt_addr = DynamicCopy(rom_start, rom_end, tail); if (virt_addr != 0) { SetSegmentBase(segment, virt_addr); } return virt_addr; } u8 *UncIndexCopy(u32 segment, u32 rom_start, u32 rom_end) { u8 *inflated; // 0x24(sp) u32 length; // 0x20(sp) u8 *mio0_copy; // 0x1C(sp) u8 *mio0_len; // 0x18(sp) inflated = NULL; length = ((rom_end - rom_start) + 0xF) & 0xFFFFFFF0; mio0_copy = _pool_alloc(length, 1); mio0_len = mio0_copy + 4; if (mio0_copy != NULL) { DmaCopy(mio0_copy, rom_start, rom_end); inflated = _pool_alloc(U32(mio0_len), 0); if (inflated != NULL) { mio0_decompress(mio0_copy, inflated); SetSegmentBase(segment, inflated); _pool_free(mio0_copy); } } return inflated; } // ---------------------------------------------------------------------------- // level scripts // ---------------------------------------------------------------------------- // process accumulator opcode u32 LevelAccumOp(u8 operation, u32 argument) // 8037E1A0/0FAF20 { u32 retVal = 0; // 4($sp) // switch/case jump table stored at 8038BAF0/108870 // 8037E1D4 8037E1EC 8037E20C 8037E228 // 8037E244 8037E25C 8037E278 8037E290 switch (operation) { case 0: retVal = (level_accum & argument); break; case 1: retVal = !(level_accum & argument); break; case 2: retVal = (level_accum == argument); break; case 3: retVal = (level_accum != argument); break; case 4: retVal = (level_accum < argument); break; case 5: retVal = (level_accum <= argument); break; case 6: retVal = (level_accum > argument); break; case 7: retVal = (level_accum >= argument); break; default: break; } return retVal; } typedef struct { u8 cmd; u8 len; u16 ramSegment; u32 romStart; u32 romEnd; u32 segmentJump; } Level00; void LevelScript00(void) // 8037E2C4/0FB044 { Level00 *l = (Level00 *)level_ptr; PushPoolState(); // 802783E8 (void)DynamicIndexCopy(l->ramSegment, l->romStart, l->romEnd, 0); // 8027868C *scriptStack = level_ptr + l->len; scriptStack += 4; *scriptStack = pushStack; scriptStack += 4; *pushStack = scriptStack; level_ptr = SegmentedToVirtual(l->segmentJump); // 80277F50 } void LevelScript02(void) // 8037E404/0FB184 { PopPoolState(); // 80278498 scriptStack = pushStack; scriptStack -= 4; pushStack = *scriptStack; scriptStack -= 4; level_ptr = *scriptStack; } typedef struct { u8 cmd; u8 len; u16 delay; } Level03; void LevelScript03(void) // 8037E47C/0FB1FC { some_flag = 0; Level03 *script = (Level03 *)level_ptr; if (level03_delay == 0) { level03_delay = script->delay; } else { level03_delay--; // also &= 0xFFFF, probably because u16 if (level03_delay == 0) { level_ptr += script->len; some_flag = 1; } } } typedef struct { u8 cmd; u8 len; u16 delay; } Level04; void LevelScript04(void) // 8037E4FC/0FB27C { some_flag = -1; Level04 *script = (Level04 *)level_ptr; if (level04_delay == 0) { level04_delay = script->delay; } else { level04_delay--; // also &= 0xFFFF, probably because u16 if (level04_delay == 0) { level_ptr += script->len; some_flag = 1; } } } typedef struct { u8 cmd; u8 len; u16 unused; u32 script; } Level05; void LevelScript05(void) // 8037E580/0FB300 { Level05 *script = (Level05 *)level_ptr; level_ptr = SegmentedToVirtual(script->script); } typedef struct { u8 cmd; u8 len; u16 unused; u32 script; } Level06; void LevelScript06(void) // 8037E5B8/0FB338 { Level06 *script = (Level06 *)level_ptr; *scriptStack = level_ptr + script->len; scriptStack += 4; level_ptr = SegmentedToVirtual(script->script); } void LevelScript07(void) // 8037E620/0FB3A0 { scriptStack -= 4; level_ptr = *scriptStack; } typedef struct { u8 cmd; u8 len; u16 param; } Level08; void LevelScript08(void) // 8037E650/0FB3D0 { Level08 *script = (Level08 *)level_ptr; *scriptStack = level_ptr + script->len; scriptStack += 4; *scriptStack = script->param; scriptStack += 4; level_ptr += script->len; } typedef struct { u8 cmd; u8 len; u16 unused; } Level09; void LevelScript09(void) // 8037E6D4/0FB454 { u32 val = *(scriptStack - 4); // 4($sp) if (val == 0) { level_ptr = *(scriptStack - 8); } else { val--; if (val != 0) { *(scriptStack - 4) = val; level_ptr = *(scriptStack - 8); } else { Level09 *script = (Level09*)level_ptr; level_ptr += script->len; scriptStack -= 8; } } } typedef struct { u8 cmd; u8 len; u16 unused; } Level0A; void LevelScript0A(void) // 8037E780/0FB500 { Level0A *script = (Level0A*)level_ptr; *scriptStack = level_ptr + script->len; scriptStack += 4; *scriptStack = 0x0; scriptStack += 4; level_ptr += script->len; } typedef struct { u8 cmd; u8 len; u8 op; u8 unused; s32 arg1; } Level0B; void LevelScript0B(void) // 8037E7F8/0FB578 { Level0B *script = (Level0B*)level_ptr; u32 retVal = LevelAccumOp(script->op, script->arg1); if (retVal != 0) { level_ptr += script->len; scriptStack -= 8; } else { level_ptr = *(scriptStack - 8); } } typedef struct { u8 cmd; u8 len; u8 op; u8 unused; s32 arg1; u32 segmentAddr; } Level0C; void LevelScript0C(void) // 8037E878/0FB5F8 { Level0C *script = (Level0C*)level_ptr; u32 retVal = LevelAccumOp(script->op, script->arg1); if (retVal != 0) { level_ptr = SegmentedToVirtual(script->segmentAddr); } else { level_ptr += script->len; } } typedef struct { u8 cmd; u8 len; u8 op; u8 unused; s32 arg1; u32 segmentAddr; } Level0D; void LevelScript0D(void) // 8037E8E8/0FB668 { Level0D *script = (Level0D*)level_ptr; u32 retVal = LevelAccumOp(script->op, script->arg1); if (retVal != 0) { *scriptStack = level_ptr + script->len; scriptStack += 4; level_ptr = SegmentedToVirtual(script->segmentAddr); } else { level_ptr += script->len; } } typedef struct { u8 cmd; u8 len; u8 op; u8 unused; s32 arg1; } Level0E; void LevelScript0E(void) // 8037E988/0FB708 { Level0E *script = (Level0E*)level_ptr; u32 retVal = LevelAccumOp(script->op, script->arg1); if (retVal == 0) { do { level_ptr += level_ptr[1]; } while (*level_ptr == 0x0F || *level_ptr == 0x10); } level_ptr += script->len; } void LevelScript0F(void) // 8037EA18/0FB798 { do { level_ptr += level_ptr[1]; } while (*level_ptr == 0x10); level_ptr += level_ptr[1]; } void LevelScript10(void) // 8037EA70/0FB7F0 { level_ptr += level_ptr[1]; } typedef struct { u8 cmd; u8 len; u16 data; u32 (*func)(u16, u32); } Level11; void LevelScript11(void) // 8037EA98/0FB818 { Level11 *script = (Level11 *)level_ptr; level_accum = script->func(script->data, level_accum); level_ptr += script->len; } u32 FixedCopy(u32 ram, u32 rom_start, u32 rom_end) // begin 802786F0/0336F0 { (void)ram; // 0x28($sp) (void)rom_start; // 0x2c($sp) (void)rom_end; // 0x30($sp) u32 ret_addr = 0; // 0x24($sp) u32 len; // 0x20($sp) u32 pool_len; // 0x1c($sp) len = ((rom_end - rom_start) + 0xF) & 0xFFFFFFF0); pool_len = (pool_tail - ram) + 0xF) & 0xFFFFFFF0); if (pool_len >= len) { ret_addr = _pool_alloc(pool_len, 1); if (ret_addr != NULL) { bzero(ret_addr, pool_len); osWriteBackDCacheAll(); DmaCopy(ret_addr, rom_start, rom_end); osInvalCache(ret_addr, pool_len); osInvalDCache(ret_addr, pool_len); } } return ret_addr; } typedef struct { u8 cmd; u8 len; u16 unused; u32 ram; u32 rom_start; u32 rom_end; } Level16; void LevelScript16(void) // 8037EC54/0FB9D4 { Level16 *script = (Level16 *)level_ptr; FixedCopy(script->ram, script->rom_start, script->rom_end, 0); level_ptr += script->len; } typedef struct { u8 cmd; u8 len; u16 segment; u32 rom_start; u32 rom_end; } Level17; void LevelScript17(void) // 8037ECA4/0FBA24 { Level17 *script = (Level17 *)level_ptr; (void)DynamicIndexCopy(script->segment, script->rom_start, script->rom_end, 0); level_ptr += script->len; } typedef struct { u8 cmd; u8 len; u16 segment; u32 rom_start; u32 rom_end; } Level18; void LevelScript18(void) // 8037ECF8/0FBA78 { Level18 *script = (Level18 *)level_ptr; (void)UncIndexCopy(script->segment, script->rom_start, script->rom_end); level_ptr += script->len; } typedef struct { u8 cmd; u8 len; u16 unused; } Level1E; void LevelScript1E(void) // 8037EF70/0FBCF0 { Level1E *script = (Level1E *)level_ptr; u32 count; // 0x1c($sp) u32 *ptr = *0x8038b8a0; // a0 proc_80278B28(ptr, ptr[1]); // *ptr, ptr+4 *0x8038b8a0 = 0; count = 0; while (count < 8) { t9 = 0x8033B8D8 + 60 * count; t9 = *t9; if (t9 != NULL) { proc_80383340(); break; } else { count++; } } level_ptr += script->len; } void InitSceneGraphNodeLinks(void *node, u16 val) //8037B220 { node->u16_00 = val; node->u16_02 = 1; node->p_04 = node; node->p_08 = node; node->p_0C = NULL; node->u32_10 = 0; } void *proc_8037BC90(void *pool, void *node, u16 layer, u32 segAddr) { // pool 0x18($sp) // node 0x1c($sp) // layer 0x20($sp) // segAddr 0x24($sp) if (pool != NULL) { node = SimpleAllocate(pool, 0x18); } if (node != NULL) { InitSceneGraphNodeLinks(node, 0x1B); node->u16_02 = (layer << 8) | (node->u16_02 & 0xFF); node->u32_14 = segAddr; } return node; } typedef struct { u8 cmd; u8 len; u16 layerAndId; u32 segmentAddr; } Level21; void LevelScript21(void) // 8037F164/0FBEE4 { u32 segAddr; // 0x18($sp) u16 layer; // 0x1c($sp) u16 id; // 0x1e($sp) Level21 *script = (Level21 *)level_ptr; id = script->layerAndId & 0xFFF; layer = script->layerAndId >> 12; segAddr = script->segmentAddr; if (id < 0x100) { geo_layout_table[id] = proc_8037BC90(0x8038b8a0, NULL, layer, segAddr); } level_ptr += script->len; } typedef struct { u8 cmd; u8 len; u16 id; u32 segmentAddr; } Level22; void LevelScript22(void) // 8037F214/0FBF94 { u32 segAddr; // 0x18($sp) u16 id; // 0x1e($sp) Level22 *script = (Level22 *)level_ptr; id = script->id; segAddr = script->segmentAddr; if (id < 0x100) { geo_layout_table[id] = ProcessGeoLayout(0x8038b8a0, segAddr); // 8037E0B4 } level_ptr += script->len; } void proc_80321668(u08 a0) // 80321668/0DC668 { u32 s1 = a0 & 0xff; /* t6 = s1 * 8; t6 += s1; t6 *= 4; t6 = t6 - s1; t6 = t6 << 5; */ t6 = a0 * 35 * 32; t7 = 0x80361FB8; t8 = t6 + t7; s0 = t8->u08_1B; s3 = 0xff; t9 = s1 * 8; t9 += s1; if (s3 != s0) { t9 *= 4; t9 -= s1; t9 <<= 5; s2 = t9 + t7; s4 = 0x1c; // 28 a0 = s1 & 0xff; do { a1 = s0 & 0xff; proc_8031EF6C(a0, a1); t0 = s0 * s4; v0 = s2 + t0; s0 = v0->u08_1B; v0->u32_14 = 0; a0 = s1 & 0xff; } while (s3 != s0); } } void proc_8032171C(void) // 8032171C/0DC71C { proc_80321668(1); proc_80321668(4); proc_80321668(6); } typedef struct { u8 cmd; u8 len; u8 param; u8 unused; } Level29; void LevelScript29(void) // 80380014/0FCD94 { void *next; // 0x18($sp) u16 param; // 0x1e($sp) Level29 *script = (Level29 *)level_ptr; param = script->param; next = level_ptr + 4; proc_8032171C(); proc_8027AE44(param); level_ptr += script->len; } typedef struct { u8 cmd; u8 len; u16 unused; } Level2A; void LevelScript2A(void) // 8038007C/0FCDFC { Level2A *script = (Level2A *)level_ptr; proc_8027AF48(); level_ptr += script->len; } typedef struct { u8 cmd; u8 len; u8 blank; u8 unused; } Level34; void LevelScript34(void) // 8037FDE4/0FCB64 { Level34 *script = (Level34 *)level_ptr; osViBlack(script->blank); // 80323340 level_ptr += script->len; }