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 = background->screenBase + yBase + (xBase >> 2); \
17 uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
18 if (UNLIKELY(!screenBlock)) { \
19 return; \
20 } \
21 LOAD_16(mapData, screenBase & VRAM_BLOCK_MASK, screenBlock); \
22 localY = inY & 0x7; \
23 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
24 localY = 7 - localY; \
25 }
26
27#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
28 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
29 palette = &mainPalette[paletteData]; \
30 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
31 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
32 if (LIKELY(vram)) { \
33 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
34 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
35 tileData >>= 4 * mod8; \
36 for (; outX < end; ++outX, ++pixel) { \
37 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
38 } \
39 } else { \
40 for (outX = end - 1; outX >= renderer->start; --outX) { \
41 uint32_t* pixel = &renderer->row[outX]; \
42 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
43 } \
44 } \
45 }
46
47#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \
48 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
49 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
50 if (UNLIKELY(!vram)) { \
51 return; \
52 } \
53 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
54 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
55 palette = &mainPalette[paletteData]; \
56 pixel = &renderer->row[outX]; \
57 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
58 if (outX < renderer->start) { \
59 tileData >>= 4 * (renderer->start - outX); \
60 outX = renderer->start; \
61 pixel = &renderer->row[outX]; \
62 } \
63 for (; outX < renderer->end; ++outX, ++pixel) { \
64 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
65 } \
66 } else { \
67 tileData >>= 4 * (0x8 - mod8); \
68 int end = renderer->end - 8; \
69 if (end < -1) { \
70 end = -1; \
71 } \
72 outX = renderer->end - 1; \
73 pixel = &renderer->row[outX]; \
74 for (; outX > end; --outX, --pixel) { \
75 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
76 } \
77 /* Needed for consistency checks */ \
78 if (VIDEO_CHECKS) { \
79 outX = renderer->end; \
80 pixel = &renderer->row[outX]; \
81 } \
82 }
83
84#define DRAW_BACKGROUND_MODE_0_MOSAIC_16(BLEND, OBJWIN) \
85 x = inX & 7; \
86 if (mosaicWait) { \
87 int baseX = x - (mosaicH - mosaicWait); \
88 if (baseX < 0) { \
89 int disturbX = (16 + baseX) >> 3; \
90 inX -= disturbX << 3; \
91 BACKGROUND_TEXT_SELECT_CHARACTER; \
92 baseX -= disturbX << 3; \
93 inX += disturbX << 3; \
94 } else { \
95 BACKGROUND_TEXT_SELECT_CHARACTER; \
96 } \
97 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
98 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
99 if (UNLIKELY(!vram)) { \
100 carryData = 0; \
101 } else { \
102 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
103 palette = &mainPalette[paletteData]; \
104 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
105 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
106 tileData >>= 4 * baseX; \
107 } else { \
108 tileData >>= 4 * (7 - baseX); \
109 } \
110 tileData &= 0xF; \
111 tileData |= tileData << 4; \
112 tileData |= tileData << 8; \
113 tileData |= tileData << 16; \
114 carryData = tileData; \
115 } \
116 } \
117 for (; length; ++tileX) { \
118 BACKGROUND_TEXT_SELECT_CHARACTER; \
119 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
120 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
121 tileData = carryData; \
122 for (; x < 8 && length; ++x, --length) { \
123 if (!mosaicWait) { \
124 if (UNLIKELY(!vram)) { \
125 carryData = 0; \
126 } else { \
127 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
128 palette = &mainPalette[paletteData]; \
129 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
130 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
131 tileData >>= x * 4; \
132 } else { \
133 tileData >>= (7 - x) * 4; \
134 } \
135 tileData &= 0xF; \
136 tileData |= tileData << 4; \
137 tileData |= tileData << 8; \
138 tileData |= tileData << 16; \
139 carryData = tileData; \
140 } \
141 mosaicWait = mosaicH; \
142 } \
143 --mosaicWait; \
144 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
145 ++pixel; \
146 } \
147 x = 0; \
148 }
149
150#define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \
151 for (; tileX < tileEnd; ++tileX) { \
152 BACKGROUND_TEXT_SELECT_CHARACTER; \
153 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
154 palette = &mainPalette[paletteData]; \
155 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
156 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
157 if (UNLIKELY(!vram)) { \
158 pixel += 8; \
159 continue; \
160 } \
161 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
162 if (tileData) { \
163 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
164 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
165 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
166 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
167 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
168 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
169 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
170 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
171 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
172 } else { \
173 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
174 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
175 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
176 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
177 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
178 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
179 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
180 BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
181 } \
182 } \
183 pixel += 8; \
184 }
185
186#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256(BLEND, OBJWIN) \
187 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
188 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
189 int end2 = end - 4; \
190 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
191 int shift = inX & 0x3; \
192 if (LIKELY(vram)) { \
193 if (end2 > outX) { \
194 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
195 tileData >>= 8 * shift; \
196 shift = 0; \
197 for (; outX < end2; ++outX, ++pixel) { \
198 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
199 } \
200 } \
201 } \
202 \
203 if (LIKELY(vram)) { \
204 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
205 tileData >>= 8 * shift; \
206 for (; outX < end; ++outX, ++pixel) { \
207 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
208 } \
209 } \
210 } else { \
211 int start = outX; \
212 outX = end - 1; \
213 pixel = &renderer->row[outX]; \
214 if (LIKELY(vram)) { \
215 if (end2 > start) { \
216 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
217 for (; outX >= end2; --outX, --pixel) { \
218 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
219 } \
220 charBase += 4; \
221 } \
222 } \
223 \
224 if (LIKELY(vram)) { \
225 LOAD_32(tileData, charBase, vram); \
226 for (; outX >= renderer->start; --outX, --pixel) { \
227 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
228 } \
229 } \
230 outX = end; \
231 pixel = &renderer->row[outX]; \
232 }
233
234#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256(BLEND, OBJWIN) \
235 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
236 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
237 if (UNLIKELY(!vram)) { \
238 return; \
239 } \
240 int end = mod8 - 4; \
241 pixel = &renderer->row[outX]; \
242 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
243 if (end > 0) { \
244 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
245 for (; outX < renderer->end - end; ++outX, ++pixel) { \
246 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
247 } \
248 charBase += 4; \
249 } \
250 \
251 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
252 for (; outX < renderer->end; ++outX, ++pixel) { \
253 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
254 } \
255 } else { \
256 int shift = (8 - mod8) & 0x3; \
257 int start = outX; \
258 outX = renderer->end - 1; \
259 pixel = &renderer->row[outX]; \
260 if (end > 0) { \
261 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
262 tileData >>= 8 * shift; \
263 for (; outX >= start + 4; --outX, --pixel) { \
264 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
265 } \
266 shift = 0; \
267 } \
268 \
269 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
270 tileData >>= 8 * shift; \
271 for (; outX >= start; --outX, --pixel) { \
272 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
273 } \
274 /* Needed for consistency checks */ \
275 if (VIDEO_CHECKS) { \
276 outX = renderer->end; \
277 pixel = &renderer->row[outX]; \
278 } \
279 }
280
281#define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \
282 for (; tileX < tileEnd; ++tileX) { \
283 BACKGROUND_TEXT_SELECT_CHARACTER; \
284 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
285 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
286 if (UNLIKELY(!vram)) { \
287 pixel += 8; \
288 continue; \
289 } \
290 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
291 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
292 if (tileData) { \
293 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
294 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
295 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
296 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
297 } \
298 pixel += 4; \
299 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
300 if (tileData) { \
301 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
302 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
303 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
304 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
305 } \
306 pixel += 4; \
307 } else { \
308 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
309 if (tileData) { \
310 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
311 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
312 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
313 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
314 } \
315 pixel += 4; \
316 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
317 if (tileData) { \
318 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
319 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
320 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
321 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
322 } \
323 pixel += 4; \
324 } \
325 }
326
327#define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \
328 for (; tileX < tileEnd; ++tileX) { \
329 BACKGROUND_TEXT_SELECT_CHARACTER; \
330 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
331 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
332 tileData = carryData; \
333 for (x = 0; x < 8; ++x) { \
334 if (!mosaicWait) { \
335 if (UNLIKELY(!vram)) { \
336 carryData = 0; \
337 } else { \
338 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
339 if (x >= 4) { \
340 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
341 tileData >>= (x - 4) * 8; \
342 } else { \
343 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
344 tileData >>= x * 8; \
345 } \
346 } else { \
347 if (x >= 4) { \
348 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
349 tileData >>= (7 - x) * 8; \
350 } else { \
351 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
352 tileData >>= (3 - x) * 8; \
353 } \
354 } \
355 tileData &= 0xFF; \
356 carryData = tileData; \
357 } \
358 mosaicWait = mosaicH; \
359 } \
360 tileData |= tileData << 8; \
361 --mosaicWait; \
362 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
363 ++pixel; \
364 } \
365 }
366
367#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_256EXT(BLEND, OBJWIN) \
368 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
369 palette = &mainPalette[paletteData]; \
370 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
371 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
372 int end2 = end - 4; \
373 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
374 int shift = inX & 0x3; \
375 if (LIKELY(vram)) { \
376 if (end2 > outX) { \
377 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
378 tileData >>= 8 * shift; \
379 shift = 0; \
380 for (; outX < end2; ++outX, ++pixel) { \
381 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
382 } \
383 } \
384 } \
385 \
386 if (LIKELY(vram)) { \
387 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
388 tileData >>= 8 * shift; \
389 for (; outX < end; ++outX, ++pixel) { \
390 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
391 } \
392 } \
393 } else { \
394 int start = outX; \
395 outX = end - 1; \
396 pixel = &renderer->row[outX]; \
397 if (LIKELY(vram)) { \
398 if (end2 > start) { \
399 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
400 for (; outX >= end2; --outX, --pixel) { \
401 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
402 } \
403 charBase += 4; \
404 } \
405 } \
406 \
407 if (LIKELY(vram)) { \
408 LOAD_32(tileData, charBase, vram); \
409 for (; outX >= renderer->start; --outX, --pixel) { \
410 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
411 } \
412 } \
413 outX = end; \
414 pixel = &renderer->row[outX]; \
415 }
416
417#define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_256EXT(BLEND, OBJWIN) \
418 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
419 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
420 if (UNLIKELY(!vram)) { \
421 return; \
422 } \
423 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
424 palette = &mainPalette[paletteData]; \
425 int end = mod8 - 4; \
426 pixel = &renderer->row[outX]; \
427 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
428 if (end > 0) { \
429 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
430 for (; outX < renderer->end - end; ++outX, ++pixel) { \
431 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
432 } \
433 charBase += 4; \
434 } \
435 \
436 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
437 for (; outX < renderer->end; ++outX, ++pixel) { \
438 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
439 } \
440 } else { \
441 int shift = (8 - mod8) & 0x3; \
442 int start = outX; \
443 outX = renderer->end - 1; \
444 pixel = &renderer->row[outX]; \
445 if (end > 0) { \
446 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
447 tileData >>= 8 * shift; \
448 for (; outX >= start + 4; --outX, --pixel) { \
449 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
450 } \
451 shift = 0; \
452 } \
453 \
454 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
455 tileData >>= 8 * shift; \
456 for (; outX >= start; --outX, --pixel) { \
457 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
458 } \
459 /* Needed for consistency checks */ \
460 if (VIDEO_CHECKS) { \
461 outX = renderer->end; \
462 pixel = &renderer->row[outX]; \
463 } \
464 }
465
466#define DRAW_BACKGROUND_MODE_0_TILES_256EXT(BLEND, OBJWIN) \
467 for (; tileX < tileEnd; ++tileX) { \
468 BACKGROUND_TEXT_SELECT_CHARACTER; \
469 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
470 palette = &mainPalette[paletteData]; \
471 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
472 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
473 if (UNLIKELY(!vram)) { \
474 pixel += 8; \
475 continue; \
476 } \
477 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
478 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
479 if (tileData) { \
480 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
481 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
482 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
483 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
484 } \
485 pixel += 4; \
486 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
487 if (tileData) { \
488 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
489 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
490 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
491 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
492 } \
493 pixel += 4; \
494 } else { \
495 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
496 if (tileData) { \
497 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
498 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
499 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
500 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
501 } \
502 pixel += 4; \
503 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
504 if (tileData) { \
505 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 3); \
506 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 2); \
507 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 1); \
508 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
509 } \
510 pixel += 4; \
511 } \
512 }
513
514#define DRAW_BACKGROUND_MODE_0_MOSAIC_256EXT(BLEND, OBJWIN) \
515 for (; tileX < tileEnd; ++tileX) { \
516 BACKGROUND_TEXT_SELECT_CHARACTER; \
517 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
518 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
519 tileData = carryData; \
520 for (x = 0; x < 8; ++x) { \
521 if (!mosaicWait) { \
522 if (UNLIKELY(!vram)) { \
523 carryData = 0; \
524 } else { \
525 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
526 palette = &mainPalette[paletteData]; \
527 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
528 if (x >= 4) { \
529 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
530 tileData >>= (x - 4) * 8; \
531 } else { \
532 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
533 tileData >>= x * 8; \
534 } \
535 } else { \
536 if (x >= 4) { \
537 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
538 tileData >>= (7 - x) * 8; \
539 } else { \
540 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
541 tileData >>= (3 - x) * 8; \
542 } \
543 } \
544 tileData &= 0xFF; \
545 carryData = tileData; \
546 } \
547 mosaicWait = mosaicH; \
548 } \
549 tileData |= tileData << 8; \
550 --mosaicWait; \
551 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
552 ++pixel; \
553 } \
554 }
555
556#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
557 uint32_t* pixel = &renderer->row[outX]; \
558 if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
559 int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
560 int x; \
561 int mosaicWait = (mosaicH - outX + renderer->masterEnd * mosaicH) % mosaicH; \
562 int carryData = 0; \
563 paletteData = 0; /* Quiets compiler warning */ \
564 DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
565 return; \
566 } \
567 \
568 if (inX & 0x7) { \
569 BACKGROUND_TEXT_SELECT_CHARACTER; \
570 \
571 int mod8 = inX & 0x7; \
572 int end = outX + 0x8 - mod8; \
573 if (end > renderer->end) { \
574 end = renderer->end; \
575 } \
576 if (UNLIKELY(end == outX)) { \
577 return; \
578 } \
579 if (UNLIKELY(end < outX)) { \
580 mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw!"); \
581 return; \
582 } \
583 DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
584 outX = end; \
585 if (tileX < tileEnd) { \
586 ++tileX; \
587 } else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
588 mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
589 return; \
590 } \
591 length -= end - renderer->start; \
592 } \
593 /*! TODO: Make sure these lines can be removed */ \
594 /*!*/ pixel = &renderer->row[outX]; \
595 outX += (tileEnd - tileX) * 8; \
596 /*!*/ if (VIDEO_CHECKS && UNLIKELY(outX > renderer->masterEnd)) { \
597 /*!*/ mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw would occur!"); \
598 /*!*/ return; \
599 /*!*/ } \
600 DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
601 if (length & 0x7) { \
602 BACKGROUND_TEXT_SELECT_CHARACTER; \
603 \
604 int mod8 = length & 0x7; \
605 if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
606 mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw!"); \
607 return; \
608 } \
609 DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
610 } \
611 if (VIDEO_CHECKS && UNLIKELY(&renderer->row[outX] != pixel)) { \
612 mLOG(GBA_VIDEO, FATAL, "Background draw ended in the wrong place! Diff: %" PRIXPTR, &renderer->row[outX] - pixel); \
613 } \
614 if (VIDEO_CHECKS && UNLIKELY(outX > renderer->masterEnd)) { \
615 mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw occurred!"); \
616 return; \
617 }
618
619void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
620 int inX = renderer->start + background->x;
621 int length = renderer->end - renderer->start;
622 if (background->mosaic) {
623 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
624 y -= y % mosaicV;
625 }
626 int inY = y + background->y;
627 uint16_t mapData;
628
629 unsigned yBase = inY & 0xF8;
630 if (background->size == 2) {
631 yBase += inY & 0x100;
632 } else if (background->size == 3) {
633 yBase += (inY & 0x100) << 1;
634 }
635 yBase <<= 3;
636
637 int localX;
638 int localY;
639
640 unsigned xBase;
641
642 uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
643 flags |= FLAG_TARGET_2 * background->target2;
644 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed));
645 objwinFlags |= flags;
646 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));
647 if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) {
648 flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
649 objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
650 }
651
652 uint32_t screenBase;
653 uint32_t charBase;
654 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
655 color_t* mainPalette;
656 if (background->multipalette && background->extPalette) {
657 mainPalette = background->extPalette;
658 // TODO
659 /*if (variant) {
660 mainPalette = background->variantPalette;
661 }*/
662 } else {
663 mainPalette = renderer->normalPalette;
664 if (variant) {
665 mainPalette = renderer->variantPalette;
666 }
667 }
668 color_t* palette = mainPalette;
669 PREPARE_OBJWIN;
670
671 int outX = renderer->start;
672
673 uint32_t tileData;
674 uint32_t current;
675 int pixelData;
676 int paletteData;
677 int tileX = 0;
678 int tileEnd = ((length + inX) >> 3) - (inX >> 3);
679 uint16_t* vram = NULL;
680
681 if (!objwinSlowPath) {
682 if (!(flags & FLAG_TARGET_2)) {
683 if (!background->multipalette) {
684 DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
685 } else if (!background->extPalette) {
686 DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
687 } else {
688 DRAW_BACKGROUND_MODE_0(256EXT, NoBlend, NO_OBJWIN);
689 }
690 } else {
691 if (!background->multipalette) {
692 DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
693 } else if (!background->extPalette) {
694 DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
695 } else {
696 DRAW_BACKGROUND_MODE_0(256EXT, Blend, NO_OBJWIN);
697 }
698 }
699 } else {
700 if (!(flags & FLAG_TARGET_2)) {
701 if (!background->multipalette) {
702 DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
703 } else if (!background->extPalette) {
704 DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
705 } else {
706 DRAW_BACKGROUND_MODE_0(256EXT, NoBlend, OBJWIN);
707 }
708 } else {
709 if (!background->multipalette) {
710 DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
711 } else if (!background->extPalette) {
712 DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
713 } else {
714 DRAW_BACKGROUND_MODE_0(256EXT, Blend, OBJWIN);
715 }
716 }
717 }
718}