From 2dbf1600284648ca6846f3b883d2c3a96fa71cf3 Mon Sep 17 00:00:00 2001 From: rengstrom Date: Wed, 9 Sep 2026 20:59:59 +0000 Subject: [PATCH 1/5] Decompile the effect_027 overlay Full-Life links eighteen of the library units, one fewer than effect_039: it carries no lib/tint, driving the four D_800EF738 entries from its own opcode handlers instead, and its entry sets up three task pools where Drain sets up four. Ring sizes 0x31 and 0x8B. 100 of its 125 functions come from src/effect/lib and 23 more are bodies already written for effect_001, effect_025, effect_028 and effect_052. The two that are new are the entry point and the bank carve, which is effect_039's with Full-Life's own constants. func_801A9490 is the exception that has to be written out rather than ported: it has the same shape as effect_025's func_801A548C but calls effectDrawScriptRun where that one calls effectStepCounter, since Full-Life links no lib/step. Masked bodies compare equal because call targets are relocations, so the callee has to be read off the aligned symbol streams. Nothing here belongs in the library. Six of the twenty-one bodies the library does not own appear more than once inside a single overlay -- one no-op reaches 169 copies -- so they are copied script handlers, not linked members. The seven that are single-copy everywhere still fail the test that decides it: a linked object's members are always adjacent, whereas these keep different neighbours in every program that carries them, and some appear without the members they would share an object with. --- Makefile | 2 +- config/ff8.yaml | 30 +- config/symbols.effect_027.txt | 112 ++++ include/effect/effect_027.h | 68 +++ include/effect/effect_027_entry.h | 19 + .../effect/params/effect_027/effect_params.h | 13 + src/effect/effect_027.c | 566 +++++++++++------- src/effect/effect_027_entry.c | 61 ++ 8 files changed, 646 insertions(+), 225 deletions(-) create mode 100644 config/symbols.effect_027.txt create mode 100644 include/effect/effect_027.h create mode 100644 include/effect/effect_027_entry.h create mode 100644 include/effect/params/effect_027/effect_params.h create mode 100644 src/effect/effect_027_entry.c diff --git a/Makefile b/Makefile index 2206756c..961fc794 100644 --- a/Makefile +++ b/Makefile @@ -140,7 +140,7 @@ CODE_OVERLAYS := field_init intro field \ tripletriad battle_render battle world ALL_EFFECT_OVERLAYS := $(filter effect_%,$(SPLAT_BINARIES)) -EFFECTS ?= effect_001 effect_025 effect_028 effect_052 effect_039 effect_056 effect_050 effect_051 effect_036 +EFFECTS ?= effect_001 effect_025 effect_028 effect_052 effect_039 effect_056 effect_050 effect_051 effect_036 effect_027 EFFECT_OVERLAYS := $(if $(filter all,$(EFFECTS)),$(ALL_EFFECT_OVERLAYS),$(EFFECTS)) OVERLAYS := $(MENU_OVERLAYS) $(CODE_OVERLAYS) $(EFFECT_OVERLAYS) ALL_OVERLAYS := $(MENU_OVERLAYS) $(CODE_OVERLAYS) $(ALL_EFFECT_OVERLAYS) diff --git a/config/ff8.yaml b/config/ff8.yaml index d87ccc74..020cae12 100644 --- a/config/ff8.yaml +++ b/config/ff8.yaml @@ -1097,16 +1097,40 @@ binaries: options: target_path: original/effect/effect_027.bin src_path: src/effect + section_order: [.text, .rodata, .data, .bss] segments: - name: effect_027 type: code start: 0x0 vram: 0x801A0000 subsegments: - - [0x0, c, effect_027] - - [0x9BB8, .rodata, effect_027] - - [0x9BCC, bin, effect_027] + - [0x0, c, effect_027_entry] + - [0x1EC, c, lib/common] + - [0x95C, c, lib/rotz] + - [0xAE8, c, lib/heading] + - [0xB18, c, lib/entity] + - [0x1688, c, lib/vector] + - [0x1DF4, c, lib/emit] + - [0x3C28, c, lib/packet] + - [0x3E30, c, lib/particlestep] + - [0x5124, c, lib/particlealloc] + - [0x5240, c, lib/draw] + - [0x5E9C, c, lib/emitteralloc] + - [0x5FBC, c, lib/spawn] + - [0x7D70, c, lib/list] + - [0x838C, c, lib/trail] + - [0x8500, c, lib/strands] + - [0x89EC, c, lib/tables] + - [0x8D00, c, lib/bankclear] + - [0x8D58, c, lib/drawscript] + - [0x91BC, c, effect_027] + - [0x9BB8, .rodata, lib/particlestep] + - [0x9BCC, pad] + - [0x9BD0, .rodata, lib/list] + - [0x9BFC, .rodata, effect_027] + - [0x9C5C, bin, effect_027] - [0x3AAF8] + - name: effect_028 sha1: eb8243db98aedc14e57e62c7b42d639c8171f9c5 options: diff --git a/config/symbols.effect_027.txt b/config/symbols.effect_027.txt new file mode 100644 index 00000000..840d55c9 --- /dev/null +++ b/config/symbols.effect_027.txt @@ -0,0 +1,112 @@ +effectTextInit = 0x801A01EC; // type:func +effectTextHex = 0x801A033C; // type:func +effectTextNewline = 0x801A03E8; // type:func +effectNop = 0x801A0408; // type:func +effectDrawQuad128 = 0x801A0410; // type:func +effectDrawQuad256 = 0x801A050C; // type:func +effectMatrixIdentity = 0x801A0608; // type:func +effectMatrixRotX = 0x801A0640; // type:func +effectMatrixRotY = 0x801A07D0; // type:func +effectMatrixRotZ = 0x801A095C; // type:func +effectMatrixHeading = 0x801A0AE8; // type:func +effectReleaseWait = 0x801A0B18; // type:func +effectSpawnTask = 0x801A0B40; // type:func +effectCacheSlotAnchors = 0x801A0CCC; // type:func +effectSlotAnchor0 = 0x801A0E00; // type:func +effectSlotAnchor1 = 0x801A0E30; // type:func +effectSlotAnchorMid = 0x801A0E60; // type:func +effectCacheAnimSlotAnchors = 0x801A0E90; // type:func +effectAnimSlotAnchor0 = 0x801A0FCC; // type:func +effectAnimSlotAnchor1 = 0x801A0FFC; // type:func +effectAnimSlotAnchorMid = 0x801A102C; // type:func +effectUpdateModelBounds = 0x801A105C; // type:func +effectGetModelBounds = 0x801A12A8; // type:func +effectModelRadius = 0x801A12F8; // type:func +effectModelHalfHeight = 0x801A139C; // type:func +effectModelHalfWidth = 0x801A13CC; // type:func +effectModelHeight = 0x801A1424; // type:func +effectModelTop = 0x801A1448; // type:func +effectModelBottom = 0x801A1490; // type:func +effectModelRandomY = 0x801A14D8; // type:func +effectModelCentreY = 0x801A1590; // type:func +effectModelCentre = 0x801A15E4; // type:func +effectZeroWords = 0x801A1650; // type:func +effectRandBetween = 0x801A1688; // type:func +effectRandProduct = 0x801A170C; // type:func +effectRandVector = 0x801A17B0; // type:func +effectMatrixFromAngles = 0x801A1828; // type:func +effectJitterAngle = 0x801A18F4; // type:func +effectJitterVector = 0x801A1988; // type:func +effectJitterAngleS = 0x801A1A00; // type:func +effectJitterSVector = 0x801A1A90; // type:func +effectCopyVector = 0x801A1B08; // type:func +effectFadeValue = 0x801A1B38; // type:func +effectFadeVector = 0x801A1B94; // type:func +effectBlendPointSets = 0x801A1C60; // type:func +effectEmitTris = 0x801A1DF4; // type:func +effectEmitQuads = 0x801A20C0; // type:func +effectEmitTexTris = 0x801A23E4; // type:func +effectEmitTexQuads = 0x801A276C; // type:func +effectEmitGouraudTris = 0x801A2B50; // type:func +effectEmitGouraudQuads = 0x801A2E58; // type:func +effectEmitGouraudTexTris = 0x801A31E0; // type:func +effectEmitGouraudTexQuads = 0x801A359C; // type:func +effectEmitStream = 0x801A39E4; // type:func +effectBuildMeshPacket = 0x801A3C28; // type:func +effectParticleLateStep = 0x801A3E30; // type:func +effectParticleAdvance = 0x801A3EE8; // type:func +effectParticleAgeHold = 0x801A46F0; // type:func +effectParticlePose = 0x801A47E4; // type:func +effectParticleAlloc = 0x801A5124; // type:func +effectStepRunKind = 0x801A5240; // type:func +effectStepFire = 0x801A5278; // type:func +effectParticleStep = 0x801A5324; // type:func +effectEmitterCountStep = 0x801A5410; // type:func +effectSpriteEmit = 0x801A5428; // type:func +effectSpritePacket = 0x801A5A54; // type:func +effectParticleDrawPacket = 0x801A5CBC; // type:func +effectDrawParticles = 0x801A5D7C; // type:func +effectEmitterAlloc = 0x801A5E9C; // type:func +effectEmitterAge = 0x801A5FBC; // type:func +effectEmitterSpawn = 0x801A6024; // type:func +effectEmitterSpawnShare = 0x801A6AC0; // type:func +effectEmitterStep = 0x801A6BC0; // type:func +effectEmitterOnStrand = 0x801A6C60; // type:func +effectEmitterOnTrail = 0x801A6EE8; // type:func +effectEmitterOnOwner = 0x801A70F0; // type:func +effectEmitterSlide = 0x801A7300; // type:func +effectListAppend = 0x801A7D70; // type:func +effectListRemove = 0x801A7DAC; // type:func +effectListClear = 0x801A7DEC; // type:func +effectListRun = 0x801A7E04; // type:func +effectEmitterSetup = 0x801A7E8C; // type:func +effectStepFireSlots = 0x801A80C0; // type:func +effectEmittersOnCue = 0x801A8174; // type:func +effectTrailPush = 0x801A82B0; // type:func +effectTrailAim = 0x801A838C; // type:func +effectStrandsSeed = 0x801A8500; // type:func +effectStrandsPush = 0x801A860C; // type:func +effectStrandsAim = 0x801A8724; // type:func +effectPoseTablesRelocate = 0x801A89EC; // type:func +effectStartChildScript = 0x801A8C90; // type:func +effectBanksClear = 0x801A8D00; // type:func +effectDrawScriptStart = 0x801A8D58; // type:func +effectDrawScriptRun = 0x801A9000; // type:func +effectDrawListSpan = 0x801A9128; // type:func +g_effectStackBase = 0x801D35BC; +g_effectStackTop = 0x801D35C0; +g_effectEmitterBank = 0x801D3864; +g_effectParticleBank = 0x801D3868; +g_effectDrawList = 0x801D386C; +g_effectEmitterCursor = 0x801D3870; +g_effectParticleCursor = 0x801D3872; +g_effectParticleTotal = 0x801D3874; +g_effectLiveTotal = 0x801D3876; +g_effectBankUnk90 = 0x801D3878; +g_effectBankUnk92 = 0x801D387A; +g_effectChildPool = 0x801D4074; +g_effectPrimCursor = 0x801D4084; +g_effectHexGlyphs = 0x801D42F4; +g_effectTextX = 0x801D4308; +g_effectTextY = 0x801D430C; +g_effectTextColour = 0x801D4310; diff --git a/include/effect/effect_027.h b/include/effect/effect_027.h new file mode 100644 index 00000000..6f851c78 --- /dev/null +++ b/include/effect/effect_027.h @@ -0,0 +1,68 @@ +#ifndef EFFECT_EFFECT_027_H +#define EFFECT_EFFECT_027_H + +#include "effect.h" +#include "effect/lib/particle.h" + +/** + * @file + * @brief Full-Life: the overlay's own storage. + */ + +/** @brief Cursor the record banks are carved off, moving up as they are taken. */ +extern u8 *D_801D35C4; + +/** @brief Sound cue the script fires on its first frame. */ +extern u8 D_801A9C5C[]; + +/** @brief Pose script the model-taking handler runs. */ +extern EffectPoseTables D_801D4314; + +/** @brief Task pools the script's children run in, and their storage. */ +extern s32 D_801D4174; +extern s32 D_801D42E4; +extern s32 D_801D3854; +extern s32 D_801D40A4; +extern s32 D_801D4184; +extern s32 D_801D3884; +extern s32 D_801D35D4; + +/** @brief Cleared when the effect starts. */ +extern s32 D_801D35B4; +extern s32 D_801D35B8; + +/** + * @name Prim buffers + * + * Two of each, so the frame being built and the frame being drawn never share. + * @{ + */ +extern void *D_801D408C; +extern void *D_801D4090; +extern void *D_801D4094; +extern void *D_801D4098; +/** @} */ + +/** @brief Cursor the effect's second prim buffer is written through. */ +extern void *D_801D4088; + +/** + * @name Overlay blob + * + * The data carried in the overlay image behind the code. + * @{ + */ + +/** @brief Table of two offsets battle.bin resolves when the effect starts. */ +extern u8 D_801A9D3C; + +/** @brief TIM uploaded to VRAM when the effect starts. */ +extern u8 D_801AA794; +/** @} */ + +/** + * @brief Run one frame of the effect: its opcode, then every task pool it owns. + */ +s32 func_801A99C8(EffectEntity *entity); + +#endif /* EFFECT_EFFECT_027_H */ diff --git a/include/effect/effect_027_entry.h b/include/effect/effect_027_entry.h new file mode 100644 index 00000000..0b48a4d4 --- /dev/null +++ b/include/effect/effect_027_entry.h @@ -0,0 +1,19 @@ +#ifndef EFFECT_EFFECT_027_ENTRY_H +#define EFFECT_EFFECT_027_ENTRY_H + +#include "effect.h" + +/** + * @file effect_027_entry.h + * @brief Full-Life: the overlay entry point. + */ + +/** + * @brief Start the effect's script and hand back its task pool. + * + * @param animSet Animation set the effect plays. + * @return The pool the root entity lives in. + */ +void *func_801A0000(EffectAnimSet *animSet); + +#endif /* EFFECT_EFFECT_027_ENTRY_H */ diff --git a/include/effect/params/effect_027/effect_params.h b/include/effect/params/effect_027/effect_params.h new file mode 100644 index 00000000..42463bb7 --- /dev/null +++ b/include/effect/params/effect_027/effect_params.h @@ -0,0 +1,13 @@ +#ifndef EFFECT_PARAMS_H +#define EFFECT_PARAMS_H + +/** + * @file effect_params.h + * @brief Full-Life: the ring sizes its particle engine is built with. + */ + +/** @brief Highest index each ring reaches before it wraps. */ +#define EFFECT_EMITTER_COUNT 0x31 +#define EFFECT_PARTICLE_COUNT 0x8B + +#endif /* EFFECT_PARAMS_H */ diff --git a/src/effect/effect_027.c b/src/effect/effect_027.c index 4b60d2b6..7632ddae 100644 --- a/src/effect/effect_027.c +++ b/src/effect/effect_027.c @@ -3,259 +3,383 @@ * @brief Full-Life */ #include "common.h" - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0000); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A01EC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A033C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A03E8); - -void func_801A0408(void) { +#include "game.h" +#include "effect.h" +#include "psxsdk/libgte.h" +#include "effect/effect_027.h" +#include "effect/lib/particle.h" +#include "effect/lib/entity.h" +#include "effect/lib/tables.h" +#include "effect/lib/drawscript.h" +#include "effect/lib/bankclear.h" +#include "effect/lib/common.h" +#include "battle.h" +#include "btl_entity.h" +#include "effect_params.h" + +/** @brief Where in the scratchpad the effect keeps its view matrix. */ +#define EFFECT_SCRATCHPAD ((MATRIX *)0x1F8002E0) + +/** @brief @ref EffectEntity::unk05C -- the frame counter's low bit picks a bank. */ +#define EFFECT_FRAME_ODD 0x1 + +/** @brief Ceiling the tint ramp clamps to, four @ref EFFECT_TINT_STEP steps up. */ +#define EFFECT_TINT_MAX 0x400 + +/** @brief How far the tint ramp moves per frame. */ +#define EFFECT_TINT_STEP 0x100 + +static void func_801A91BC(EffectEntity *entity); +static void func_801A9208(EffectEntity *entity); +static void func_801A9270(EffectEntity *entity); +static s32 func_801A9278(EffectEntity *entity); +static void func_801A9318(EffectEntity *entity); +static void func_801A9330(EffectEntity *entity); +static void func_801A938C(EffectEntity *entity); +static s32 func_801A9394(EffectEntity *entity); +static void func_801A9434(EffectEntity *entity); +static void func_801A9490(EffectEntity *entity); +static void func_801A94D8(EffectEntity *entity); +static s32 func_801A94E0(EffectEntity *entity); +static void func_801A9580(EffectEntity *entity); +static void func_801A95D8(EffectEntity *entity); +static void func_801A960C(EffectEntity *entity); +static void func_801A9694(EffectEntity *entity); +static s32 func_801A969C(EffectEntity *entity); +static void func_801A9778(EffectEntity *entity); +static void func_801A978C(EffectEntity *entity); +static void func_801A97A0(EffectEntity *entity); +static void func_801A9830(EffectEntity *entity); +static void func_801A9888(EffectEntity *entity); +static void func_801A98EC(EffectEntity *entity); +static void func_801A9954(EffectEntity *entity); +static void func_801A997C(EffectEntity *entity); +static void func_801A9990(EffectEntity *entity); +static void func_801A99A4(EffectEntity *entity); +static void func_801A99C0(EffectEntity *entity); + +/** @brief Opcode handler: clear the screen tint. */ +static void func_801A91BC(EffectEntity *entity) { + BattleTint *tint = D_800EF738; + s32 i; + + entity->pos.vx = 0; + for (i = 0; i < 4; i++) { + tint->level = entity->pos.vx; + tint->b = 0; + tint->g = 0; + tint->r = 0; + tint++; + } + entity->pc++; } -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0410); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A050C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0608); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0640); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A07D0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A095C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0AE8); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0B18); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0B40); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0CCC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0E00); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0E30); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0E60); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0E90); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0FCC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A0FFC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A102C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A105C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A12A8); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A12F8); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A139C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A13CC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1424); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1448); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1490); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A14D8); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1590); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A15E4); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1650); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1688); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A170C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A17B0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1828); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A18F4); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1988); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1A00); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1A90); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1B08); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1B38); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1B94); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1C60); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A1DF4); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A20C0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A23E4); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A276C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A2B50); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A2E58); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A31E0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A359C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A39E4); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A3C28); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A3E30); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A3EE8); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A46F0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A47E4); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5124); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5240); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5278); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5324); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5410); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5428); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5A54); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5CBC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5D7C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5E9C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A5FBC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A6024); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A6AC0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A6BC0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A6C60); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A6EE8); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A70F0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A7300); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A7D70); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A7DAC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A7DEC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A7E04); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A7E8C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A80C0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A8174); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A82B0); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A838C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A8500); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A860C); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A8724); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A89EC); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A8C90); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A8D00); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A8D58); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9000); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9128); - -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A91BC); +/** @brief Opcode handler: ramp the screen tint up to the maximum, then stop. */ +static void func_801A9208(EffectEntity *entity) { + BattleTint *tint = D_800EF738; + s32 i; + + entity->pos.vx += EFFECT_TINT_STEP; + if (entity->pos.vx >= EFFECT_TINT_MAX) { + entity->pos.vx = EFFECT_TINT_MAX; + entity->flags |= EFFECT_FLAG_STOP; + entity->pc++; + } + for (i = 3; i >= 0; i--) { + tint->level = entity->pos.vx; + tint++; + } +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9208); +/** @brief Opcode handler: no-op. */ +static void func_801A9270(EffectEntity *entity) { +} -void func_801A9270(void) { +/** + * @brief Script dispatcher for the tint fade-in. + * + * @return 2 once the script has stopped and its children have drained, 0 while + * it is still running. + */ +static s32 func_801A9278(EffectEntity *entity) { + EffectHandler handlers[3] = { func_801A91BC, func_801A9208, func_801A9270 }; + + handlers[entity->pc](entity); + entity->unk024++; + if (entity->flags & EFFECT_FLAG_STOP) { + if (entity->wait != 0) { + return 0; + } + effectReleaseWait(entity); + return 2; + } + return 0; } -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9278); +/** @brief Opcode handler: hold the screen tint at the maximum. */ +static void func_801A9318(EffectEntity *entity) { + entity->pos.vx = EFFECT_TINT_MAX; + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9318); +/** @brief Opcode handler: ramp the screen tint back to zero, then stop. */ +static void func_801A9330(EffectEntity *entity) { + BattleTint *tint = D_800EF738; + s32 i; + + entity->pos.vx -= EFFECT_TINT_STEP; + if (entity->pos.vx <= 0) { + entity->pos.vx = 0; + entity->flags |= EFFECT_FLAG_STOP; + entity->pc++; + } + for (i = 3; i >= 0; i--) { + tint->level = entity->pos.vx; + tint++; + } +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9330); +/** @brief Opcode handler: no-op. */ +static void func_801A938C(EffectEntity *entity) { +} -void func_801A938C(void) { +/** + * @brief Script dispatcher for the tint fade-out. + * + * @return 2 once the script has stopped and its children have drained, 0 while + * it is still running. + */ +static s32 func_801A9394(EffectEntity *entity) { + EffectHandler handlers[3] = { func_801A9318, func_801A9330, func_801A938C }; + + handlers[entity->pc](entity); + entity->unk024++; + if (entity->flags & EFFECT_FLAG_STOP) { + if (entity->wait != 0) { + return 0; + } + effectReleaseWait(entity); + return 2; + } + return 0; } -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9394); +/** @brief Opcode handler: draw and retire once the frame count is reached. */ +static void func_801A9434(EffectEntity *entity) { + EffectDrawScript *script = (EffectDrawScript *)entity; -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9434); + if (entity->unk024 >= script->stopFrame) { + /* Called with no argument: the entity is already in $a0 and stays there. */ + ((void (*)())effectDrawScriptStart)(); + effectDrawScriptRun(script); + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9490); +/** @brief Opcode handler: hold still until the draw script finishes. */ +static void func_801A9490(EffectEntity *entity) { + if (effectDrawScriptRun((EffectDrawScript *)entity) != 0) { + entity->flags |= EFFECT_FLAG_STOP; + entity->pc++; + } +} -void func_801A94D8(void) { +/** @brief Opcode handler: no-op. */ +static void func_801A94D8(EffectEntity *entity) { } -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A94E0); +/** + * @brief Script dispatcher for the draw script. + * + * @return 2 once the script has stopped and its children have drained, 0 while + * it is still running. + */ +static s32 func_801A94E0(EffectEntity *entity) { + EffectHandler handlers[3] = { func_801A9434, func_801A9490, func_801A94D8 }; + + handlers[entity->pc](entity); + entity->unk024++; + if (entity->flags & EFFECT_FLAG_STOP) { + if (entity->wait != 0) { + return 0; + } + effectReleaseWait(entity); + return 2; + } + return 0; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9580); +/** @brief Opcode handler: start the pose script on the taken model. */ +static void func_801A9580(EffectEntity *entity) { + effectStartChildScript(entity, func_801A94E0, &D_801D4314, 0, 0x2D, 0); + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A95D8); +/** @brief Opcode handler: release the model once the count reaches 30. */ +static void func_801A95D8(EffectEntity *entity) { + if (entity->unk024 == 30) { + entity->unk010->unk063 = 0; + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A960C); +/** @brief Opcode handler: hand the current part to battle, then stop. */ +static void func_801A960C(EffectEntity *entity) { + if (entity->unk024 >= 30) { + func_800BFE1C(&entity->animSet->anims[entity->unk02A].parts[entity->unk02B]); + entity->flags |= EFFECT_FLAG_STOP; + entity->pc++; + } +} -void func_801A9694(void) { +/** @brief Opcode handler: no-op. */ +static void func_801A9694(EffectEntity *entity) { } -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A969C); +/** @brief Run one frame of this effect's script and report whether it ended. */ +static s32 func_801A969C(EffectEntity *entity) { + EffectHandler handlers[4] = { func_801A9580, func_801A95D8, func_801A960C, + func_801A9694 }; + + effectCacheSlotAnchors(entity); + effectUpdateModelBounds(entity); + handlers[entity->pc](entity); + if (entity->unk024 == 0) { + func_800C4764(D_801A9C5C, 0, 0x80); + } + entity->unk024++; + if (entity->flags & EFFECT_FLAG_STOP) { + if (entity->wait != 0) { + return 0; + } + effectReleaseWait(entity); + return 2; + } + return 0; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9778); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A9778(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A978C); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A978C(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A97A0); +/** @brief Opcode handler: carve both record banks and start the draw script. */ +static void func_801A97A0(EffectEntity *entity) { + if (entity->wait == 0) { + /* Both banks are carved out of the same byte arena behind the TIM. */ + g_effectEmitterBank = (EffectEmitter *)D_801D35C4; + D_801D35C4 += EFFECT_EMITTER_BANK_BYTES; + g_effectParticleBank = (EffectParticle *)D_801D35C4; + D_801D35C4 += EFFECT_PARTICLE_BANK_BYTES; + effectBanksClear(); + effectSpawnTask(&D_801D3854, func_801A9278, 0x40, entity); + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9830); +/** @brief Opcode handler: start a task running @ref func_801A969C. */ +static void func_801A9830(EffectEntity *entity) { + entity->unk063 = 1; + effectSpawnTask(&D_801D42E4, func_801A969C, 0x58, entity); + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9888); +/** @brief Opcode handler: repeat the step until the loop counter runs out. */ +static void func_801A9888(EffectEntity *entity) { + if (entity->unk063 == 0) { + if (entity->unk02A < entity->unk058) { + entity->unk02A++; + entity->unk02E++; + entity->pc--; + } else { + /* The byte trio at unk060 is this script's countdown, one halfword wide. */ + *(s16 *)entity->unk060 = 10; + entity->pc++; + } + } +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A98EC); +/** @brief Opcode handler: spawn the tint fade-out once the wait runs out. */ +static void func_801A98EC(EffectEntity *entity) { + /* The byte trio at unk060 is this script's countdown, one halfword wide. */ + if (--*(s16 *)entity->unk060 <= 0) { + effectSpawnTask(&D_801D3854, func_801A9394, 0x40, entity); + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9954); +/** @brief Opcode handler: stall here until @c unk05E reaches zero. */ +static void func_801A9954(EffectEntity *entity) { + if (entity->unk05E == 0) { + entity->pc++; + } +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A997C); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A997C(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A9990); +/** @brief Opcode handler: consume the opcode and do nothing else. */ +static void func_801A9990(EffectEntity *entity) { + entity->pc++; +} -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A99A4); +/** @brief Opcode handler: raise @ref EFFECT_FLAG_STOP and consume the opcode. */ +static void func_801A99A4(EffectEntity *entity) { + entity->flags |= EFFECT_FLAG_STOP; + entity->pc++; +} -void func_801A99C0(void) { +/** @brief Opcode handler: no-op. */ +static void func_801A99C0(EffectEntity *entity) { } -INCLUDE_ASM("asm/ovl/effect_027/nonmatchings/effect_027", func_801A99C8); +/** + * @brief Run one frame of the effect: its opcode, then every task pool it owns. + * + * @param entity The script's root entity. + * @return 2 once the script has stopped and its children have drained, 0 while + * it is still running. + */ +s32 func_801A99C8(EffectEntity *entity) { + EffectHandler handlers[11] = { + func_801A9778, func_801A978C, func_801A97A0, func_801A9830, func_801A9888, + func_801A98EC, func_801A9954, func_801A997C, func_801A9990, func_801A99A4, + func_801A99C0 + }; + MATRIX *view = EFFECT_SCRATCHPAD; + + *view = D_800F02C8; + g_effectStackTop = (u8 *)view; + g_effectStackBase = view; + if (entity->unk05C & EFFECT_FRAME_ODD) { + g_effectPrimCursor = D_801D408C; + D_801D4088 = D_801D4094; + } else { + g_effectPrimCursor = D_801D4090; + D_801D4088 = D_801D4098; + } + effectCacheAnimSlotAnchors(entity); + handlers[entity->pc](entity); + g_effectParticleTotal = 0; + entity->unk05E = 0; + g_effectLiveTotal = 0; + entity->unk05E += func_800B2B68(&D_801D42E4); + entity->unk05E += func_800B2B68(&g_effectChildPool); + entity->unk05E += func_800B2B68(&D_801D3854); + entity->unk05C++; + entity->unk024++; + if (entity->flags & EFFECT_FLAG_STOP) { + if (entity->wait == 0) { + effectReleaseWait(entity); + return 2; + } + } + return 0; +} diff --git a/src/effect/effect_027_entry.c b/src/effect/effect_027_entry.c new file mode 100644 index 00000000..727e07ff --- /dev/null +++ b/src/effect/effect_027_entry.c @@ -0,0 +1,61 @@ +/** + * @file effect_027_entry.c + * @brief Full-Life: the overlay entry point. + */ +#include "common.h" +#include "effect.h" +#include "effect/lib/entity.h" +#include "effect/lib/tables.h" +#include "effect/effect_027.h" +#include "effect/effect_027_entry.h" + +/** @brief Stride between the prim and frame banks carved out of the TIM. */ +#define EFFECT_BANK_SIZE 0x6000 + +/** + * @brief Start the effect's script and hand back its task pool. + * + * @param animSet Animation set the effect plays. + * @return The pool the root entity lives in. + */ +void *func_801A0000(EffectAnimSet *animSet) { + u8 *bank0; + EffectEntity *entity; + u8 *bank1; + u8 *frames; + + D_801D35B4 = 0; + D_801D35B8 = 0; + func_800B2A00(&D_801D4174, &D_801D40A4, 0x64, 2); + entity = effectSpawnTask(&D_801D4174, func_801A99C8, 0x64, NULL); + entity->animSet = animSet; + entity->unk02C = animSet->anims[entity->unk02A].unk000; + entity->unk02D = + entity->animSet->anims[entity->unk02A].parts[entity->unk02B].unk000; + entity->pc = 0; + entity->unk05A = animSet->anims->unk010; + entity->unk058 = animSet->anims->unk011; + entity->unk02F = entity->unk05A - 1; + if (entity->unk02F < entity->unk058) { + entity->unk02F = entity->unk058; + } + if (!(animSet->flags & EFFECT_ANIMSET_FLAG_LOADED)) { + func_800C3BE0(&D_801A9D3C); + func_800BB084(&D_801AA794); + } + D_801D35C4 = &D_801AA794; + bank0 = &D_801AA794; + D_801D408C = bank0; + bank1 = bank0 + EFFECT_BANK_SIZE; + D_801D4094 = bank0 + EFFECT_BANK_SIZE; + D_801D35C4 = bank0 + EFFECT_BANK_SIZE; + frames = bank1; + D_801D4090 = frames; + frames += EFFECT_BANK_SIZE; + D_801D35C4 = frames; + D_801D4098 = frames; + func_800B2A00(&D_801D42E4, &D_801D4184, 0x58, 4); + func_800B2A00(&g_effectChildPool, &D_801D3884, 0x2A4, 3); + func_800B2A00(&D_801D3854, &D_801D35D4, 0x40, 0xA); + return &D_801D4174; +} From dc78c89c874fd176102a30a8b195bbeb7e7427f6 Mon Sep 17 00:00:00 2001 From: rengstrom Date: Wed, 9 Sep 2026 21:14:02 +0000 Subject: [PATCH 2/5] Count the tint fade loops upwards The four tint entries are walked through a cursor, so the loop index is never read; it was written as a countdown only because that is the shape gcc emits. The ascending form is what the surrounding handlers use and assembles the same. lib/common.c keeps its countdown: that one indexes text[i] and fills the hex digits from the least significant nibble, so its direction carries meaning. --- src/effect/effect_025.c | 4 ++-- src/effect/effect_027.c | 4 ++-- src/effect/effect_028.c | 4 ++-- src/effect/effect_039.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/effect/effect_025.c b/src/effect/effect_025.c index f707faa9..8ff0a161 100644 --- a/src/effect/effect_025.c +++ b/src/effect/effect_025.c @@ -279,7 +279,7 @@ static void func_801A71B8(EffectEntity *entity) { entity->pos.vx = 0x600; entity->pc++; } - for (i = 3; i >= 0; i--) { + for (i = 0; i < 4; i++) { tint->level = entity->pos.vx; tint++; } @@ -304,7 +304,7 @@ static void func_801A7240(EffectEntity *entity) { entity->flags |= EFFECT_FLAG_STOP; entity->pc++; } - for (i = 3; i >= 0; i--) { + for (i = 0; i < 4; i++) { tint->level = entity->pos.vx; tint++; } diff --git a/src/effect/effect_027.c b/src/effect/effect_027.c index 7632ddae..d4ecf6c1 100644 --- a/src/effect/effect_027.c +++ b/src/effect/effect_027.c @@ -85,7 +85,7 @@ static void func_801A9208(EffectEntity *entity) { entity->flags |= EFFECT_FLAG_STOP; entity->pc++; } - for (i = 3; i >= 0; i--) { + for (i = 0; i < 4; i++) { tint->level = entity->pos.vx; tint++; } @@ -133,7 +133,7 @@ static void func_801A9330(EffectEntity *entity) { entity->flags |= EFFECT_FLAG_STOP; entity->pc++; } - for (i = 3; i >= 0; i--) { + for (i = 0; i < 4; i++) { tint->level = entity->pos.vx; tint++; } diff --git a/src/effect/effect_028.c b/src/effect/effect_028.c index 929f8133..5dcc75ce 100644 --- a/src/effect/effect_028.c +++ b/src/effect/effect_028.c @@ -86,7 +86,7 @@ static void func_801A9500(EffectEntity *entity) { entity->flags |= EFFECT_FLAG_STOP; entity->pc++; } - for (i = 3; i >= 0; i--) { + for (i = 0; i < 4; i++) { tint->level = entity->pos.vx; tint++; } @@ -134,7 +134,7 @@ static void func_801A9628(EffectEntity *entity) { entity->flags |= EFFECT_FLAG_STOP; entity->pc++; } - for (i = 3; i >= 0; i--) { + for (i = 0; i < 4; i++) { tint->level = entity->pos.vx; tint++; } diff --git a/src/effect/effect_039.c b/src/effect/effect_039.c index c7af5bf2..264e07a0 100644 --- a/src/effect/effect_039.c +++ b/src/effect/effect_039.c @@ -80,7 +80,7 @@ static void func_801A9500(EffectEntity *entity) { entity->flags |= EFFECT_FLAG_STOP; entity->pc++; } - for (i = 3; i >= 0; i--) { + for (i = 0; i < 4; i++) { tint->level = entity->pos.vx; tint++; } @@ -128,7 +128,7 @@ static void func_801A9628(EffectEntity *entity) { entity->flags |= EFFECT_FLAG_STOP; entity->pc++; } - for (i = 3; i >= 0; i--) { + for (i = 0; i < 4; i++) { tint->level = entity->pos.vx; tint++; } From 139414f0449405eaacb7eaecf3518ee8286c0b6d Mon Sep 17 00:00:00 2001 From: rengstrom Date: Wed, 9 Sep 2026 21:17:25 +0000 Subject: [PATCH 3/5] Say why the fill and rotation clears count down Both walk a cursor and never read the index, which is exactly the shape that invites rewriting them ascending. Doing so assembles differently and effect_025 stops matching, so the countdown has to stay and should say why. --- src/effect/lib/fill.c | 7 ++++++- src/effect/lib/rot.c | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/effect/lib/fill.c b/src/effect/lib/fill.c index 284c5e71..8a0ab764 100644 --- a/src/effect/lib/fill.c +++ b/src/effect/lib/fill.c @@ -6,7 +6,12 @@ #include "effect.h" #include "effect/lib/fill.h" -/** @brief Fill @p count words at @p dst with @p value. */ +/** + * @brief Fill @p count words at @p dst with @p value. + * + * The index is never read, but counting down to @c -1 is load-bearing: written + * ascending it assembles differently and effect_025 stops matching. + */ void effectFillWords(s32 *dst, s32 value, s32 count) { s32 i; diff --git a/src/effect/lib/rot.c b/src/effect/lib/rot.c index 2efe4010..ec011153 100644 --- a/src/effect/lib/rot.c +++ b/src/effect/lib/rot.c @@ -1,6 +1,10 @@ /** * @file rot.c * @brief Rotation matrices built from an angle. + * + * The three builders clear the matrix through a countdown whose index is never + * read. Written ascending it assembles differently and effect_025 stops + * matching, so the `i != 0` form is load-bearing. */ #include "common.h" #include "effect.h" From 250e20ca952d0fc7e61b236e4aad638b94323bbf Mon Sep 17 00:00:00 2001 From: rengstrom Date: Wed, 9 Sep 2026 21:18:58 +0000 Subject: [PATCH 4/5] State the countdown rule without naming one binary Only effect_025 links these units today, so citing it as the thing that breaks would read as if the other overlays were free to rewrite the loop. What holds is that the ascending form generates different code. --- src/effect/lib/fill.c | 5 +++-- src/effect/lib/rot.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/effect/lib/fill.c b/src/effect/lib/fill.c index 8a0ab764..38557ed8 100644 --- a/src/effect/lib/fill.c +++ b/src/effect/lib/fill.c @@ -9,8 +9,9 @@ /** * @brief Fill @p count words at @p dst with @p value. * - * The index is never read, but counting down to @c -1 is load-bearing: written - * ascending it assembles differently and effect_025 stops matching. + * The index is never read, which makes it look like a loop that should count + * up. It is not: the ascending form generates different code, so counting down + * to @c -1 is load-bearing. */ void effectFillWords(s32 *dst, s32 value, s32 count) { s32 i; diff --git a/src/effect/lib/rot.c b/src/effect/lib/rot.c index ec011153..6f44658f 100644 --- a/src/effect/lib/rot.c +++ b/src/effect/lib/rot.c @@ -3,8 +3,8 @@ * @brief Rotation matrices built from an angle. * * The three builders clear the matrix through a countdown whose index is never - * read. Written ascending it assembles differently and effect_025 stops - * matching, so the `i != 0` form is load-bearing. + * read, which makes it look like a loop that should count up. It is not: the + * ascending form generates different code, so `i != 0` is load-bearing. */ #include "common.h" #include "effect.h" From 9cf9536d28751256d0c4d150a0635507822c2e07 Mon Sep 17 00:00:00 2001 From: rengstrom Date: Wed, 9 Sep 2026 21:20:46 +0000 Subject: [PATCH 5/5] Drop the backticks from the rot.c note Nothing else in the effect sources marks up code in a doc comment that way. --- src/effect/lib/rot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/effect/lib/rot.c b/src/effect/lib/rot.c index 6f44658f..2d475252 100644 --- a/src/effect/lib/rot.c +++ b/src/effect/lib/rot.c @@ -4,7 +4,7 @@ * * The three builders clear the matrix through a countdown whose index is never * read, which makes it look like a loop that should count up. It is not: the - * ascending form generates different code, so `i != 0` is load-bearing. + * ascending form generates different code, so the countdown is load-bearing. */ #include "common.h" #include "effect.h"