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 "gba/renderers/software-private.h"
7
8#include <mgba/internal/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, 0); \
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, 0); \
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, 0); \
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, 0); \
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, 0); \
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, 0); \
151 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
152 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
153 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
154 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
155 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
156 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
157 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
158 } else { \
159 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
160 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
161 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
162 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
163 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
164 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
165 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
166 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
167 } \
168 } \
169 pixel += 8; \
170 }
171
172#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
173 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
174 int end2 = end - 4; \
175 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
176 int shift = inX & 0x3; \
177 if (LIKELY(charBase < 0x10000)) { \
178 if (end2 > outX) { \
179 LOAD_32(tileData, charBase, vram); \
180 tileData >>= 8 * shift; \
181 shift = 0; \
182 for (; outX < end2; ++outX, ++pixel) { \
183 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
184 } \
185 } \
186 } \
187 \
188 if (LIKELY(charBase < 0x10000)) { \
189 LOAD_32(tileData, charBase + 4, vram); \
190 tileData >>= 8 * shift; \
191 for (; outX < end; ++outX, ++pixel) { \
192 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
193 } \
194 } \
195 } else { \
196 int start = outX; \
197 outX = end - 1; \
198 pixel = &renderer->row[outX]; \
199 if (LIKELY(charBase < 0x10000)) { \
200 if (end2 > start) { \
201 LOAD_32(tileData, charBase, vram); \
202 for (; outX >= end2; --outX, --pixel) { \
203 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
204 } \
205 charBase += 4; \
206 } \
207 } \
208 \
209 if (LIKELY(charBase < 0x10000)) { \
210 LOAD_32(tileData, charBase, vram); \
211 for (; outX >= renderer->start; --outX, --pixel) { \
212 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
213 } \
214 } \
215 outX = end; \
216 pixel = &renderer->row[outX]; \
217 }
218
219#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
220 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
221 if (UNLIKELY(charBase >= 0x10000)) { \
222 return; \
223 } \
224 int end = mod8 - 4; \
225 pixel = &renderer->row[outX]; \
226 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
227 if (end > 0) { \
228 LOAD_32(tileData, charBase, vram); \
229 for (; outX < renderer->end - end; ++outX, ++pixel) { \
230 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
231 } \
232 charBase += 4; \
233 } \
234 \
235 LOAD_32(tileData, charBase, vram); \
236 for (; outX < renderer->end; ++outX, ++pixel) { \
237 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
238 } \
239 } else { \
240 int shift = (8 - mod8) & 0x3; \
241 int start = outX; \
242 outX = renderer->end - 1; \
243 pixel = &renderer->row[outX]; \
244 if (end > 0) { \
245 LOAD_32(tileData, charBase, vram); \
246 tileData >>= 8 * shift; \
247 for (; outX >= start + 4; --outX, --pixel) { \
248 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
249 } \
250 shift = 0; \
251 } \
252 \
253 LOAD_32(tileData, charBase + 4, vram); \
254 tileData >>= 8 * shift; \
255 for (; outX >= start; --outX, --pixel) { \
256 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
257 } \
258 /* Needed for consistency checks */ \
259 if (VIDEO_CHECKS) { \
260 outX = renderer->end; \
261 pixel = &renderer->row[outX]; \
262 } \
263 }
264
265#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
266 for (; tileX < tileEnd; ++tileX) { \
267 BACKGROUND_TEXT_SELECT_CHARACTER; \
268 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
269 if (UNLIKELY(charBase >= 0x10000)) { \
270 pixel += 8; \
271 continue; \
272 } \
273 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
274 LOAD_32(tileData, charBase, vram); \
275 if (tileData) { \
276 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
277 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
278 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
279 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
280 } \
281 pixel += 4; \
282 LOAD_32(tileData, charBase + 4, vram); \
283 if (tileData) { \
284 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
285 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
286 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
287 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
288 } \
289 pixel += 4; \
290 } else { \
291 LOAD_32(tileData, charBase + 4, vram); \
292 if (tileData) { \
293 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
294 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
295 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
296 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
297 } \
298 pixel += 4; \
299 LOAD_32(tileData, charBase, vram); \
300 if (tileData) { \
301 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
302 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
303 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
304 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
305 } \
306 pixel += 4; \
307 } \
308 }
309
310#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
311 for (; tileX < tileEnd; ++tileX) { \
312 BACKGROUND_TEXT_SELECT_CHARACTER; \
313 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
314 tileData = carryData; \
315 for (x = 0; x < 8; ++x) { \
316 if (!mosaicWait) { \
317 if (UNLIKELY(charBase >= 0x10000)) { \
318 carryData = 0; \
319 } else { \
320 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
321 if (x >= 4) { \
322 LOAD_32(tileData, charBase + 4, vram); \
323 tileData >>= (x - 4) * 8; \
324 } else { \
325 LOAD_32(tileData, charBase, vram); \
326 tileData >>= x * 8; \
327 } \
328 } else { \
329 if (x >= 4) { \
330 LOAD_32(tileData, charBase, vram); \
331 tileData >>= (7 - x) * 8; \
332 } else { \
333 LOAD_32(tileData, charBase + 4, vram); \
334 tileData >>= (3 - x) * 8; \
335 } \
336 } \
337 tileData &= 0xFF; \
338 carryData = tileData; \
339 } \
340 mosaicWait = mosaicH; \
341 } \
342 tileData |= tileData << 8; \
343 --mosaicWait; \
344 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
345 ++pixel; \
346 } \
347 }
348
349#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
350 uint32_t* pixel = &renderer->row[outX]; \
351 if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
352 int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
353 int x; \
354 int mosaicWait = (mosaicH - outX + VIDEO_HORIZONTAL_PIXELS * mosaicH) % mosaicH; \
355 int carryData = 0; \
356 paletteData = 0; /* Quiets compiler warning */ \
357 DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
358 return; \
359 } \
360 \
361 if (inX & 0x7) { \
362 BACKGROUND_TEXT_SELECT_CHARACTER; \
363 \
364 int mod8 = inX & 0x7; \
365 int end = outX + 0x8 - mod8; \
366 if (end > renderer->end) { \
367 end = renderer->end; \
368 } \
369 if (UNLIKELY(end == outX)) { \
370 return; \
371 } \
372 if (UNLIKELY(end < outX)) { \
373 mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw!"); \
374 return; \
375 } \
376 DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
377 outX = end; \
378 if (tileX < tileEnd) { \
379 ++tileX; \
380 } else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
381 mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
382 return; \
383 } \
384 length -= end - renderer->start; \
385 } \
386 /*! TODO: Make sure these lines can be removed */ \
387 /*!*/ pixel = &renderer->row[outX]; \
388 outX += (tileEnd - tileX) * 8; \
389 /*!*/ if (VIDEO_CHECKS && UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
390 /*!*/ mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw would occur!"); \
391 /*!*/ return; \
392 /*!*/ } \
393 DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
394 if (length & 0x7) { \
395 BACKGROUND_TEXT_SELECT_CHARACTER; \
396 \
397 int mod8 = length & 0x7; \
398 if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
399 mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw!"); \
400 return; \
401 } \
402 DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
403 } \
404 if (VIDEO_CHECKS && UNLIKELY(&renderer->row[outX] != pixel)) { \
405 mLOG(GBA_VIDEO, FATAL, "Background draw ended in the wrong place! Diff: %" PRIXPTR, &renderer->row[outX] - pixel); \
406 } \
407 if (VIDEO_CHECKS && UNLIKELY(outX > VIDEO_HORIZONTAL_PIXELS)) { \
408 mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw occurred!"); \
409 return; \
410 }
411
412void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
413 int inX = renderer->start + background->x;
414 int length = renderer->end - renderer->start;
415 if (background->mosaic) {
416 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
417 y -= y % mosaicV;
418 }
419 int inY = y + background->y;
420 uint16_t mapData;
421
422 unsigned yBase = inY & 0xF8;
423 if (background->size == 2) {
424 yBase += inY & 0x100;
425 } else if (background->size == 3) {
426 yBase += (inY & 0x100) << 1;
427 }
428 yBase = (background->screenBase >> 1) + (yBase << 2);
429
430 int localX;
431 int localY;
432
433 unsigned xBase;
434
435 uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
436 flags |= FLAG_TARGET_2 * background->target2;
437 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed));
438 objwinFlags |= flags;
439 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));
440 if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) {
441 flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
442 objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
443 }
444
445 uint32_t screenBase;
446 uint32_t charBase;
447 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
448 color_t* mainPalette = renderer->normalPalette;
449 if (variant) {
450 mainPalette = renderer->variantPalette;
451 }
452 color_t* palette = mainPalette;
453 PREPARE_OBJWIN;
454
455 int outX = renderer->start;
456
457 uint32_t tileData;
458 uint32_t current;
459 int pixelData;
460 int paletteData;
461 int tileX = 0;
462 int tileEnd = ((length + inX) >> 3) - (inX >> 3);
463 uint16_t* vram = renderer->d.vram;
464
465 if (!objwinSlowPath) {
466 if (!(flags & FLAG_TARGET_2)) {
467 if (!background->multipalette) {
468 DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
469 } else {
470 DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
471 }
472 } else {
473 if (!background->multipalette) {
474 DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
475 } else {
476 DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
477 }
478 }
479 } else {
480 if (!(flags & FLAG_TARGET_2)) {
481 if (!background->multipalette) {
482 DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
483 } else {
484 DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
485 }
486 } else {
487 if (!background->multipalette) {
488 DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
489 } else {
490 DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
491 }
492 }
493 }
494}