src/gba/renderers/software-mode0.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "software-private.h"
7
8#include "gba/gba.h"
9
10#define BACKGROUND_TEXT_SELECT_CHARACTER \
11 localX = tileX * 8 + inX; \
12 xBase = localX & 0xF8; \
13 if (background->size & 1) { \
14 xBase += (localX & 0x100) << 5; \
15 } \
16 screenBase = yBase + (xBase >> 3); \
17 LOAD_16(mapData, screenBase << 1, vram); \
18 localY = inY & 0x7; \
19 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
20 localY = 7 - localY; \
21 }
22
23#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
24 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
25 palette = &mainPalette[paletteData]; \
26 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
27 LOAD_32(tileData, charBase, vram); \
28 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
29 tileData >>= 4 * mod8; \
30 for (; outX < end; ++outX, ++pixel) { \
31 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
32 } \
33 } else { \
34 for (outX = end - 1; outX >= renderer->start; --outX) { \
35 uint32_t* pixel = &renderer->row[outX]; \
36 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
37 } \
38 }
39
40#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
41 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
42 LOAD_32(tileData, charBase, vram); \
43 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
44 palette = &mainPalette[paletteData]; \
45 pixel = &renderer->row[outX]; \
46 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
47 if (outX < renderer->start) { \
48 tileData >>= 4 * (renderer->start - outX); \
49 outX = renderer->start; \
50 pixel = &renderer->row[outX]; \
51 } \
52 for (; outX < renderer->end; ++outX, ++pixel) { \
53 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
54 } \
55 } else { \
56 tileData >>= 4 * (0x8 - mod8); \
57 int end = renderer->end - 8; \
58 if (end < -1) { \
59 end = -1; \
60 } \
61 outX = renderer->end - 1; \
62 pixel = &renderer->row[outX]; \
63 for (; outX > end; --outX, --pixel) { \
64 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
65 } \
66 /* Needed for consistency checks */ \
67 if (VIDEO_CHECKS) { \
68 outX = renderer->end; \
69 pixel = &renderer->row[outX]; \
70 } \
71 }
72
73#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
74 x = inX & 7; \
75 if (mosaicWait) { \
76 int baseX = x - (mosaicH - mosaicWait); \
77 if (baseX < 0) { \
78 int disturbX = (16 + baseX) >> 3; \
79 inX -= disturbX << 3; \
80 BACKGROUND_TEXT_SELECT_CHARACTER; \
81 baseX -= disturbX << 3; \
82 inX += disturbX << 3; \
83 } else { \
84 BACKGROUND_TEXT_SELECT_CHARACTER; \
85 } \
86 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
87 if (UNLIKELY(charBase >= 0x10000)) { \
88 carryData = 0; \
89 } else { \
90 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
91 palette = &mainPalette[paletteData]; \
92 LOAD_32(tileData, charBase, vram); \
93 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
94 tileData >>= 4 * baseX; \
95 } else { \
96 tileData >>= 4 * (7 - baseX); \
97 } \
98 tileData &= 0xF; \
99 tileData |= tileData << 4; \
100 tileData |= tileData << 8; \
101 tileData |= tileData << 16; \
102 carryData = tileData; \
103 } \
104 } \
105 for (; length; ++tileX) { \
106 BACKGROUND_TEXT_SELECT_CHARACTER; \
107 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
108 tileData = carryData; \
109 for (; x < 8 && length; ++x, --length) { \
110 if (!mosaicWait) { \
111 if (UNLIKELY(charBase >= 0x10000)) { \
112 carryData = 0; \
113 } else { \
114 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
115 palette = &mainPalette[paletteData]; \
116 LOAD_32(tileData, charBase, vram); \
117 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
118 tileData >>= x * 4; \
119 } else { \
120 tileData >>= (7 - x) * 4; \
121 } \
122 tileData &= 0xF; \
123 tileData |= tileData << 4; \
124 tileData |= tileData << 8; \
125 tileData |= tileData << 16; \
126 carryData = tileData; \
127 } \
128 mosaicWait = mosaicH; \
129 } \
130 --mosaicWait; \
131 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
132 ++pixel; \
133 } \
134 x = 0; \
135 }
136
137#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
138 for (; tileX < tileEnd; ++tileX) { \
139 BACKGROUND_TEXT_SELECT_CHARACTER; \
140 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
141 palette = &mainPalette[paletteData]; \
142 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
143 if (UNLIKELY(charBase >= 0x10000)) { \
144 pixel += 8; \
145 continue; \
146 } \
147 LOAD_32(tileData, charBase, vram); \
148 if (tileData) { \
149 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
150 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
151 ++pixel; \
152 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
153 ++pixel; \
154 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
155 ++pixel; \
156 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
157 ++pixel; \
158 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
159 ++pixel; \
160 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
161 ++pixel; \
162 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
163 ++pixel; \
164 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
165 ++pixel; \
166 } else { \
167 pixel += 7; \
168 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
169 --pixel; \
170 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
171 --pixel; \
172 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
173 --pixel; \
174 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
175 --pixel; \
176 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
177 --pixel; \
178 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
179 --pixel; \
180 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
181 --pixel; \
182 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \
183 pixel += 8; \
184 } \
185 } else { \
186 pixel += 8; \
187 } \
188 }
189
190#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
191 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
192 int end2 = end - 4; \
193 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
194 int shift = inX & 0x3; \
195 if (LIKELY(charBase < 0x10000)) { \
196 if (end2 > outX) { \
197 LOAD_32(tileData, charBase, vram); \
198 tileData >>= 8 * shift; \
199 shift = 0; \
200 for (; outX < end2; ++outX, ++pixel) { \
201 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
202 } \
203 } \
204 } \
205 \
206 if (LIKELY(charBase < 0x10000)) { \
207 LOAD_32(tileData, charBase + 4, vram); \
208 tileData >>= 8 * shift; \
209 for (; outX < end; ++outX, ++pixel) { \
210 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
211 } \
212 } \
213 } else { \
214 int start = outX; \
215 outX = end - 1; \
216 pixel = &renderer->row[outX]; \
217 if (LIKELY(charBase < 0x10000)) { \
218 if (end2 > start) { \
219 LOAD_32(tileData, charBase, vram); \
220 for (; outX >= end2; --outX, --pixel) { \
221 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
222 } \
223 charBase += 4; \
224 } \
225 } \
226 \
227 if (LIKELY(charBase < 0x10000)) { \
228 LOAD_32(tileData, charBase, vram); \
229 for (; outX >= renderer->start; --outX, --pixel) { \
230 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
231 } \
232 } \
233 outX = end; \
234 pixel = &renderer->row[outX]; \
235 }
236
237#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
238 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
239 if (UNLIKELY(charBase >= 0x10000)) { \
240 return; \
241 } \
242 int end = mod8 - 4; \
243 pixel = &renderer->row[outX]; \
244 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
245 if (end > 0) { \
246 LOAD_32(tileData, charBase, vram); \
247 for (; outX < renderer->end - end; ++outX, ++pixel) { \
248 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
249 } \
250 charBase += 4; \
251 } \
252 \
253 LOAD_32(tileData, charBase, vram); \
254 for (; outX < renderer->end; ++outX, ++pixel) { \
255 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
256 } \
257 } else { \
258 int shift = (8 - mod8) & 0x3; \
259 int start = outX; \
260 outX = renderer->end - 1; \
261 pixel = &renderer->row[outX]; \
262 if (end > 0) { \
263 LOAD_32(tileData, charBase, vram); \
264 tileData >>= 8 * shift; \
265 for (; outX >= start + 4; --outX, --pixel) { \
266 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
267 } \
268 shift = 0; \
269 } \
270 \
271 LOAD_32(tileData, charBase + 4, vram); \
272 tileData >>= 8 * shift; \
273 for (; outX >= start; --outX, --pixel) { \
274 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
275 } \
276 /* Needed for consistency checks */ \
277 if (VIDEO_CHECKS) { \
278 outX = renderer->end; \
279 pixel = &renderer->row[outX]; \
280 } \
281 }
282
283#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
284 for (; tileX < tileEnd; ++tileX) { \
285 BACKGROUND_TEXT_SELECT_CHARACTER; \
286 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
287 if (UNLIKELY(charBase >= 0x10000)) { \
288 pixel += 8; \
289 continue; \
290 } \
291 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
292 LOAD_32(tileData, charBase, vram); \
293 if (tileData) { \
294 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
295 ++pixel; \
296 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
297 ++pixel; \
298 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
299 ++pixel; \
300 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
301 ++pixel; \
302 } else { \
303 pixel += 4; \
304 } \
305 LOAD_32(tileData, charBase + 4, vram); \
306 if (tileData) { \
307 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
308 ++pixel; \
309 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
310 ++pixel; \
311 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
312 ++pixel; \
313 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
314 ++pixel; \
315 } else { \
316 pixel += 4; \
317 } \
318 } else { \
319 LOAD_32(tileData, charBase + 4, vram); \
320 if (tileData) { \
321 pixel += 3; \
322 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
323 --pixel; \
324 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
325 --pixel; \
326 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
327 --pixel; \
328 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
329 } \
330 pixel += 4; \
331 LOAD_32(tileData, charBase, vram); \
332 if (tileData) { \
333 pixel += 3; \
334 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
335 --pixel; \
336 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
337 --pixel; \
338 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
339 --pixel; \
340 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
341 } \
342 pixel += 4; \
343 } \
344 }
345
346#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
347 for (; tileX < tileEnd; ++tileX) { \
348 BACKGROUND_TEXT_SELECT_CHARACTER; \
349 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
350 tileData = carryData; \
351 for (x = 0; x < 8; ++x) { \
352 if (!mosaicWait) { \
353 if (UNLIKELY(charBase >= 0x10000)) { \
354 carryData = 0; \
355 } else { \
356 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
357 if (x >= 4) { \
358 LOAD_32(tileData, charBase + 4, vram); \
359 tileData >>= (x - 4) * 8; \
360 } else { \
361 LOAD_32(tileData, charBase, vram); \
362 tileData >>= x * 8; \
363 } \
364 } else { \
365 if (x >= 4) { \
366 LOAD_32(tileData, charBase, vram); \
367 tileData >>= (7 - x) * 8; \
368 } else { \
369 LOAD_32(tileData, charBase + 4, vram); \
370 tileData >>= (3 - x) * 8; \
371 } \
372 } \
373 tileData &= 0xFF; \
374 carryData = tileData; \
375 } \
376 mosaicWait = mosaicH; \
377 } \
378 tileData |= tileData << 8; \
379 --mosaicWait; \
380 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \
381 ++pixel; \
382 } \
383 }
384
385#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
386 uint32_t* pixel = &renderer->row[outX]; \
387 if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
388 int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
389 int x; \
390 int mosaicWait = (mosaicH - outX + VIDEO_HORIZONTAL_PIXELS * mosaicH) % mosaicH; \
391 int carryData = 0; \
392 paletteData = 0; /* Quiets compiler warning */ \
393 DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
394 return; \
395 } \
396 \
397 if (inX & 0x7) { \
398 BACKGROUND_TEXT_SELECT_CHARACTER; \
399 \
400 int mod8 = inX & 0x7; \
401 int end = outX + 0x8 - mod8; \
402 if (end > renderer->end) { \
403 end = renderer->end; \
404 } \
405 if (UNLIKELY(end == outX)) { \
406 return; \
407 } \
408 if (UNLIKELY(end < outX)) { \
409 GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw!"); \
410 return; \
411 } \
412 DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
413 outX = end; \
414 if (tileX < tileEnd) { \
415 ++tileX; \
416 } else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
417 GBALog(0, GBA_LOG_FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
418 return; \
419 } \
420 length -= end - renderer->start; \
421 } \
422 /*! TODO: Make sure these lines can be removed */ \
423 /*!*/ pixel = &renderer->row[outX]; \
424 outX += (tileEnd - tileX) * 8; \
425 /*!*/ if (VIDEO_CHECKS && UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
426 /*!*/ GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw would occur!"); \
427 /*!*/ return; \
428 /*!*/ } \
429 DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
430 if (length & 0x7) { \
431 BACKGROUND_TEXT_SELECT_CHARACTER; \
432 \
433 int mod8 = length & 0x7; \
434 if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
435 GBALog(0, GBA_LOG_FATAL, "Invariant doesn't hold in background draw!"); \
436 return; \
437 } \
438 DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
439 } \
440 if (VIDEO_CHECKS && UNLIKELY(&renderer->row[outX] != pixel)) { \
441 GBALog(0, GBA_LOG_FATAL, "Background draw ended in the wrong place! Diff: %" PRIXPTR, &renderer->row[outX] - pixel); \
442 } \
443 if (VIDEO_CHECKS && UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
444 GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw occurred!"); \
445 return; \
446 }
447
448void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
449 int inX = renderer->start + background->x;
450 int length = renderer->end - renderer->start;
451 if (background->mosaic) {
452 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
453 y -= y % mosaicV;
454 }
455 int inY = y + background->y;
456 uint16_t mapData;
457
458 unsigned yBase = inY & 0xF8;
459 if (background->size == 2) {
460 yBase += inY & 0x100;
461 } else if (background->size == 3) {
462 yBase += (inY & 0x100) << 1;
463 }
464 yBase = (background->screenBase >> 1) + (yBase << 2);
465
466 int localX;
467 int localY;
468
469 unsigned xBase;
470
471 int flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
472 flags |= FLAG_TARGET_2 * background->target2;
473 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed));
474 objwinFlags |= flags;
475 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));
476
477 uint32_t screenBase;
478 uint32_t charBase;
479 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
480 color_t* mainPalette = renderer->normalPalette;
481 if (variant) {
482 mainPalette = renderer->variantPalette;
483 }
484 color_t* palette = mainPalette;
485 PREPARE_OBJWIN;
486
487 int outX = renderer->start;
488
489 uint32_t tileData;
490 uint32_t current;
491 int pixelData;
492 int paletteData;
493 int tileX = 0;
494 int tileEnd = ((length + inX) >> 3) - (inX >> 3);
495 uint16_t* vram = renderer->d.vram;
496
497 if (!objwinSlowPath) {
498 if (!(flags & FLAG_TARGET_2) && renderer->blendEffect != BLEND_ALPHA) {
499 if (!background->multipalette) {
500 DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
501 } else {
502 DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
503 }
504 } else {
505 if (!background->multipalette) {
506 DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
507 } else {
508 DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
509 }
510 }
511 } else {
512 if (!(flags & FLAG_TARGET_2) && renderer->blendEffect != BLEND_ALPHA) {
513 if (!background->multipalette) {
514 DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
515 } else {
516 DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
517 }
518 } else {
519 if (!background->multipalette) {
520 DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
521 } else {
522 DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
523 }
524 }
525 }
526}