Alchemy
Loading...
Searching...
No Matches

blitter.c

Go to the documentation of this file.
1/// @file
2/// The blitter builder, BattleFx_BuildBlitter.
3/// Part of effect.c, the battle effect object, compiled only through it. The
4/// folder README describes the routine it writes, block by block, under
5/// "Blitters"; the fragments' registers and modes are described at the top of
6/// battle_effect_composite.s.
7
9
10/// The 24-bit displacement field of an ARM branch at `site` to `target`: in
11/// words, counted from the branch plus 8 bytes, as the processor reads two
12/// words ahead.
13#define BRANCH_FIELD(site, target) \
14 ((((u32)(target) - (u32)(site) - 8) >> 2) & 0x00FFFFFF)
15
16/// The words of a partial run, one pixel at a time: the mode's one-pixel
17/// fragment, read forwards or, mirrored across, backwards. Each walk writes a
18/// leading and a trailing run.
19#define BLITTER_PARTIAL_RUN(mode, flags) \
20 switch (mode) { \
21 default: \
22 case COMPOSITE_COPY: \
23 if (((flags) & COMPOSITE_FLIP_X) != 0) { \
24 COPY_FROM(BattleFx_CopyReverse, 2); \
25 } else { \
26 COPY_FROM(BattleFx_CopyForward, 2); \
27 } \
28 break; \
29 case COMPOSITE_TRANSPARENT: \
30 if (((flags) & COMPOSITE_FLIP_X) != 0) { \
31 COPY_FROM(BattleFx_TransparentReverse, 4); \
32 } else { \
33 COPY_FROM(BattleFx_TransparentForward, 4); \
34 } \
35 break; \
36 case COMPOSITE_MAX: \
37 if (((flags) & COMPOSITE_FLIP_X) != 0) { \
38 COPY_FROM(BattleFx_MaxReverse, 4); \
39 } else { \
40 COPY_FROM(BattleFx_MaxForward, 4); \
41 } \
42 break; \
43 case COMPOSITE_ADDITIVE: \
44 if (((flags) & COMPOSITE_FLIP_X) != 0) { \
45 COPY_FROM(BattleFx_AdditiveReverse, 6); \
46 } else { \
47 COPY_FROM(BattleFx_AdditiveForward, 6); \
48 } \
49 break; \
50 }
51
52/// The template walk itself, written once. BattleFx_BuildBlitter expands it
53/// twice under two definitions of PUT, COPY, COPY_FROM, COPY_TWICE,
54/// PUT_BRANCH, MARK and PATCH: first to count the words, then to write them.
55/// It uses the caller's templateWords, flags, mode, widthShift, heightShift,
56/// and the branch sites emptyRowsBranch, emptyColumnsBranch, rowLoop,
57/// skipBranch, partialRun and fullCellLoop.
58#define BLITTER_WALK() \
59 COPY(3); \
60 if (mode == COMPOSITE_ADDITIVE) { \
61 COPY_FROM(BattleFx_Mask, 3); \
62 } \
63 \
64 if ((flags & COMPOSITE_FLIP) == COMPOSITE_FLIP_X) { \
65 COPY_FROM(templateWords, 3); \
66 } \
67 templateWords += 3; \
68 if ((flags & COMPOSITE_FLIP) == COMPOSITE_FLIP_Y) { \
69 COPY_FROM(templateWords, 4); \
70 } \
71 templateWords += 4; \
72 if ((flags & COMPOSITE_FLIP) == COMPOSITE_FLIP) { \
73 COPY_FROM(templateWords, 3); \
74 } \
75 templateWords += 3; \
76 if ((flags & COMPOSITE_FLIP) == 0) { \
77 PUT(templateWords[0]); \
78 } \
79 templateWords += 1; \
80 \
81 if ((flags & COMPOSITE_CLIP_Y) != 0) { \
82 PUT(templateWords[0]); \
83 PUT(templateWords[1]); \
84 if ((flags & COMPOSITE_FLIP_Y) != 0) { \
85 PUT(templateWords[2]); \
86 } else { \
87 PUT(templateWords[3]); \
88 } \
89 PUT(templateWords[4]); \
90 PUT(templateWords[5]); \
91 PUT(templateWords[6] + (1 << heightShift)); \
92 PUT(templateWords[7] + (1 << heightShift)); \
93 } \
94 templateWords += 8; \
95 \
96 PUT(*templateWords++); \
97 MARK(emptyRowsBranch); \
98 PUT(*templateWords++); \
99 \
100 if ((flags & COMPOSITE_CLIP_X) == 0) { \
101 PUT(templateWords[0] + (1 << widthShift) - 1); \
102 } \
103 templateWords += 1; \
104 if ((flags & COMPOSITE_CLIP_X) != 0) { \
105 PUT(templateWords[0]); \
106 PUT(templateWords[1]); \
107 if ((flags & COMPOSITE_FLIP_X) != 0) { \
108 PUT(templateWords[2]); \
109 PUT(templateWords[3]); \
110 } else { \
111 PUT(templateWords[4]); \
112 PUT(templateWords[5]); \
113 } \
114 PUT(templateWords[6]); \
115 PUT(templateWords[7]); \
116 PUT(templateWords[8] + gBattleFx_BitOperands[widthShift]); \
117 if ((flags & COMPOSITE_FLIP_X) != 0) { \
118 PUT(templateWords[9]); \
119 } else { \
120 PUT(templateWords[10]); \
121 } \
122 PUT(templateWords[11] + gBattleFx_BitOperands[widthShift]); \
123 } \
124 templateWords += 12; \
125 \
126 PUT(*templateWords++); \
127 MARK(emptyColumnsBranch); \
128 PUT(*templateWords++); \
129 \
130 COPY(6); \
131 \
132 PUT(*templateWords++); \
133 if ((flags & COMPOSITE_CLIP_X) == 0) { \
134 PUT(templateWords[0]); \
135 } \
136 templateWords += 1; \
137 \
138 COPY(5); \
139 \
140 MARK(rowLoop); \
141 PUT(templateWords[0]); \
142 PUT(templateWords[1] + (1 << (heightShift - 3)) - 1); \
143 PUT(templateWords[2] + ((widthShift - 3) << 7)); \
144 PUT(templateWords[3]); \
145 templateWords += 4; \
146 \
147 if ((flags & COMPOSITE_CLIP_X) == 0) { \
148 PUT(templateWords[0]); \
149 } \
150 templateWords += 1; \
151 \
152 PUT(*templateWords++); \
153 MARK(skipBranch); \
154 PUT(*templateWords++); \
155 if ((flags & COMPOSITE_FLIP_X) != 0) { \
156 PUT(templateWords[0]); \
157 } else { \
158 PUT(templateWords[1]); \
159 } \
160 templateWords += 2; \
161 \
162 MARK(partialRun); \
163 BLITTER_PARTIAL_RUN(mode, flags); \
164 templateWords += 4; \
165 \
166 PUT(*templateWords++); \
167 PUT_BRANCH(partialRun); \
168 PUT(*templateWords++); \
169 if ((flags & COMPOSITE_CLIP_X) == 0) { \
170 PUT(templateWords[0]); \
171 PUT(templateWords[1]); \
172 } \
173 templateWords += 2; \
174 PATCH(skipBranch); \
175 \
176 PUT(*templateWords++); \
177 MARK(skipBranch); \
178 PUT(*templateWords++); \
179 \
180 MARK(fullCellLoop); \
181 /* The words of the full-cell loop body, eight pixels per pass: the copy \
182 * fragment eight times, or the mode's 8-pixel fragment (the 4-pixel maximum \
183 * and colour fragments twice). */ \
184 switch (mode) { \
185 default: \
186 case COMPOSITE_COPY: { \
187 s32 i; \
188 \
189 for (i = 0; i < 8; i++) { \
190 if ((flags & COMPOSITE_FLIP_X) != 0) { \
191 COPY_FROM(BattleFx_CopyReverse, 2); \
192 } else { \
193 COPY_FROM(BattleFx_CopyForward, 2); \
194 } \
195 } \
196 } break; \
197 case COMPOSITE_TRANSPARENT: \
198 if ((flags & COMPOSITE_FLIP_X) != 0) { \
199 COPY_FROM(BattleFx_Transparent8Reverse, 25); \
200 } else { \
201 COPY_FROM(BattleFx_Transparent8Forward, 25); \
202 } \
203 break; \
204 case COMPOSITE_MAX: \
205 if ((flags & COMPOSITE_FLIP_X) != 0) { \
206 COPY_TWICE(BattleFx_Max4Reverse, 16); \
207 } else { \
208 COPY_TWICE(BattleFx_Max4Forward, 16); \
209 } \
210 break; \
211 case COMPOSITE_ADDITIVE: \
212 if ((flags & COMPOSITE_FLIP_X) != 0) { \
213 COPY_TWICE(BattleFx_ColorReverse, 14); \
214 } else { \
215 COPY_TWICE(BattleFx_ColorForward, 14); \
216 } \
217 break; \
218 } \
219 templateWords += 4; \
220 \
221 PUT(*templateWords++); \
222 if ((flags & COMPOSITE_CLIP_X) == 0) { \
223 PUT(templateWords[0]); \
224 PUT(templateWords[1]); \
225 } \
226 templateWords += 2; \
227 \
228 PUT(*templateWords++); \
229 PUT_BRANCH(fullCellLoop); \
230 PATCH(skipBranch); \
231 \
232 PUT(*templateWords++); \
233 MARK(skipBranch); \
234 PUT(*templateWords++); \
235 if ((flags & COMPOSITE_FLIP_X) != 0) { \
236 PUT(templateWords[0]); \
237 } else { \
238 PUT(templateWords[1]); \
239 } \
240 templateWords += 2; \
241 \
242 MARK(partialRun); \
243 BLITTER_PARTIAL_RUN(mode, flags); \
244 templateWords += 4; \
245 \
246 PUT(*templateWords++); \
247 PUT_BRANCH(partialRun); \
248 PATCH(skipBranch); \
249 \
250 COPY(3); \
251 \
252 PUT_BRANCH(rowLoop); \
253 PATCH(emptyRowsBranch); \
254 PATCH(emptyColumnsBranch); \
255 \
256 PUT(*templateWords++); \
257 PUT(*templateWords++)
258
259/// Writes a drawing routine for the canvas into heap slot `slot`, built from
260/// the template BattleFx_Composite for one clip, mirroring and blend mode.
261///
262/// Called through EFFECT_LOAD_BLITTER by nearly every canvas effect and by
263/// BattleFx_FetchRectangleBlitters, directly by the particle streams for a
264/// canvas 256 pixels wide, and from other banks through its far stub,
265/// BattleEffect_LoadWorkFar (the star field in battle/star_field.c and
266/// object/colorfx.c).
267///
268/// It allocates the slot's block (from IWRAM when it fits) at exactly the
269/// routine's size. A slot that already holds a block keeps it, whatever its
270/// size, and the routine is written over it: callers release the slot first
271/// when the new routine may be longer. The caller calls the slot's block as a
272/// DrawRectangle.
273///
274/// widthShift, heightShift: the canvas's width and height as powers of two.
275/// flags: the COMPOSITE_CLIP_ and COMPOSITE_FLIP_ bits.
276/// mode: how source pixels combine with the canvas (COMPOSITE_COPY,
277/// COMPOSITE_TRANSPARENT, COMPOSITE_MAX or COMPOSITE_ADDITIVE).
278///
279/// Returns nonzero when the walk ended exactly at gBattleFx_MoteCells, the end
280/// of the template; no caller reads it.
281s32 BattleFx_BuildBlitter(s32 slot, s32 widthShift, s32 heightShift, s32 flags,
282 u32 mode) {
283 const u32 *templateWords;
284 u32 *codeWords;
285 u32 *emptyRowsBranch;
286 u32 *emptyColumnsBranch;
287 u32 *rowLoop;
288 u32 *skipBranch;
289 u32 *partialRun;
290 u32 *fullCellLoop;
291 const u32 *codeEnd;
292 s32 wordCount;
293
294#define PUT(word) wordCount++
295#define COPY(n) \
296 wordCount += (n); \
297 templateWords += (n)
298#define COPY_FROM(from, n) wordCount += (n)
299#define COPY_TWICE(from, n) \
300 wordCount += (n); \
301 wordCount += (n)
302#define PUT_BRANCH(target) wordCount++
303#define MARK(site)
304#define PATCH(site)
305
306 // First walk: count the words selected by clipping, mirroring and blend
307 // mode. Branch sites are only marked in the second walk, so this one needs
308 // no buffer.
309 wordCount = 0;
310 templateWords = BattleFx_Composite;
311
312 BLITTER_WALK();
313
314#undef PUT
315#undef COPY
316#undef COPY_FROM
317#undef COPY_TWICE
318#undef PUT_BRANCH
319#undef MARK
320#undef PATCH
321
322 /* FAKEMATCH: one-pass blocks keep each writing step together and in list
323 * order while allowing scheduling within it; written as plain statements,
324 * PUT differs 38 in all six editions. */
325#define PUT(word) \
326 do { \
327 *codeWords++ = (word); \
328 } while (0)
329 /* FAKEMATCH: a one-pass block, as PUT's tag explains; written as plain
330 * statements, COPY differs 12 in all six editions. */
331#define COPY(n) \
332 do { \
333 Dma_Set(templateWords, codeWords, DMA_COPY_WORDS(n), REG_DMA3); \
334 codeWords += (n); \
335 templateWords += (n); \
336 } while (0)
337#define COPY_FROM(from, n) \
338 Dma_Set(from, codeWords, DMA_COPY_WORDS(n), REG_DMA3); \
339 codeWords += (n)
340#define COPY_TWICE(from, n) \
341 { \
342 const u32 *part = (from); \
343 Dma_Set(part, codeWords, DMA_COPY_WORDS(n), REG_DMA3); \
344 codeWords += (n); \
345 Dma_Set(part, codeWords, DMA_COPY_WORDS(n), REG_DMA3); \
346 codeWords += (n); \
347 }
348#define PUT_BRANCH(target) \
349 *codeWords = *templateWords++ + BRANCH_FIELD(codeWords, target); \
350 codeWords++
351#define MARK(site) site = codeWords
352 /* FAKEMATCH: a one-pass block, as PUT's tag explains; written as a plain
353 * statement, PATCH differs 2 in all six editions. */
354#define PATCH(site) \
355 do { \
356 *(site) |= BRANCH_FIELD(site, codeWords); \
357 } while (0)
358
359 // Second walk: allocate, copy the selected fragments with DMA 3 and patch
360 // their branches. A forward branch is written with its template word, its
361 // site marked, and its displacement ORed in when the walk reaches its
362 // target; a backward branch is written whole at once.
363 codeWords = (u32 *)Runtime_AllocateHeapBlock(slot, wordCount << 2);
364 templateWords = BattleFx_Composite;
365
366 BLITTER_WALK();
367
368 // The template is the last thing in battle_effect_composite.s, which the
369 // object list links straight before effect_table.c, whose first table is
370 // gBattleFx_MoteCells: a complete walk ends at that table's address.
371 codeEnd = (const u32 *)gBattleFx_MoteCells;
372 return templateWords == codeEnd;
373}
374
375// These macros describe the two walks above; they are not an effect API.
376#undef PUT
377#undef COPY
378#undef COPY_FROM
379#undef COPY_TWICE
380#undef PUT_BRANCH
381#undef MARK
382#undef PATCH
383#undef BLITTER_PARTIAL_RUN
384#undef BLITTER_WALK
385#undef BRANCH_FIELD
const u32 BattleFx_Composite[]
#define BLITTER_WALK()
The template walk itself, written once.
Definition blitter.c:58
s32 BattleFx_BuildBlitter(s32 slot, s32 widthShift, s32 heightShift, s32 flags, u32 mode)
Writes a drawing routine for the canvas into heap slot slot, built from the template BattleFx_Composi...
Definition blitter.c:281
What every part of the battle effect object shares: its includes, macros and frame-loop helpers.
u16 gBattleFx_MoteCells[]
Mote cells: particle sheets hold ten, cell n (from 0) n + 1 pixels wide and twice that tall.
void * Runtime_AllocateHeapBlock(s32 kind, s32 size)
Returns heap slot kind's block, first allocating size bytes for it (rounded up to words),...
Definition heap.c:47
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15