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