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