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 x = inX & 7; \
477 if (mosaicWait) { \
478 int baseX = x - (mosaicH - mosaicWait); \
479 if (baseX < 0) { \
480 int disturbX = (16 + baseX) >> 3; \
481 inX -= disturbX << 3; \
482 localX = tileX * 8 + inX; \
483 BACKGROUND_TEXT_SELECT_CHARACTER; \
484 localY = inY & 0x7; \
485 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
486 localY = 7 - localY; \
487 } \
488 baseX -= disturbX << 3; \
489 inX += disturbX << 3; \
490 } else { \
491 localX = tileX * 8 + inX; \
492 BACKGROUND_TEXT_SELECT_CHARACTER; \
493 localY = inY & 0x7; \
494 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
495 localY = 7 - localY; \
496 } \
497 } \
498 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
499 if (UNLIKELY(charBase >= 0x10000)) { \
500 carryData = 0; \
501 } else { \
502 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
503 LOAD_32(tileData, charBase, vram); \
504 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
505 if (x >= 4) { \
506 LOAD_32(tileData, charBase + 4, vram); \
507 tileData >>= (x - 4) * 8; \
508 } else { \
509 LOAD_32(tileData, charBase, vram); \
510 tileData >>= x * 8; \
511 } \
512 } else { \
513 if (x >= 4) { \
514 LOAD_32(tileData, charBase, vram); \
515 tileData >>= (7 - x) * 8; \
516 } else { \
517 LOAD_32(tileData, charBase + 4, vram); \
518 tileData >>= (3 - x) * 8; \
519 } \
520 } \
521 tileData &= 0xFF; \
522 carryData = tileData; \
523 } \
524 } \
525 localX = tileX * 8 + inX; \
526 for (; length; ++tileX) { \
527 mapData = background->mapCache[(localX >> 3) & 0x3F]; \
528 localX += 8; \
529 localY = inY & 0x7; \
530 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
531 localY = 7 - localY; \
532 } \
533 charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \
534 vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
535 tileData = carryData; \
536 for (x = 0; x < 8 && length; ++x, --length) { \
537 if (!mosaicWait) { \
538 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
539 palette = &mainPalette[paletteData]; \
540 if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
541 if (x >= 4) { \
542 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
543 tileData >>= (x - 4) * 8; \
544 } else { \
545 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
546 tileData >>= x * 8; \
547 } \
548 } else { \
549 if (x >= 4) { \
550 LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
551 tileData >>= (7 - x) * 8; \
552 } else { \
553 LOAD_32(tileData, (charBase + 4) & VRAM_BLOCK_MASK, vram); \
554 tileData >>= (3 - x) * 8; \
555 } \
556 } \
557 tileData &= 0xFF; \
558 carryData = tileData; \
559 mosaicWait = mosaicH; \
560 } \
561 tileData |= tileData << 8; \
562 --mosaicWait; \
563 BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN, 0); \
564 ++outX; \
565 } \
566 }
567
568#define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \
569 if (background->mosaic && GBAMosaicControlGetBgH(renderer->mosaic)) { \
570 int mosaicH = GBAMosaicControlGetBgH(renderer->mosaic) + 1; \
571 int x; \
572 int mosaicWait = (mosaicH - outX + renderer->masterEnd * mosaicH) % mosaicH; \
573 int carryData = 0; \
574 paletteData = 0; /* Quiets compiler warning */ \
575 DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \
576 return; \
577 } \
578 \
579 if (inX & 0x7) { \
580 localX = tileX * 8 + inX; \
581 BACKGROUND_TEXT_SELECT_CHARACTER; \
582 localY = inY & 0x7; \
583 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
584 localY = 7 - localY; \
585 } \
586 int mod8 = inX & 0x7; \
587 int end = outX + 0x8 - mod8; \
588 if (end > renderer->end) { \
589 end = renderer->end; \
590 } \
591 if (UNLIKELY(end == outX)) { \
592 return; \
593 } \
594 if (UNLIKELY(end < outX)) { \
595 mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw!"); \
596 return; \
597 } \
598 DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \
599 outX = end; \
600 if (tileX < tileEnd) { \
601 ++tileX; \
602 } else if (VIDEO_CHECKS && UNLIKELY(tileX > tileEnd)) { \
603 mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw! tileX (%u) > tileEnd (%u)", tileX, tileEnd); \
604 return; \
605 } \
606 length -= end - renderer->start; \
607 } \
608 localX = (tileX * 8 + inX) & 0x1FF; \
609 DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \
610 if (length & 0x7) { \
611 localX = tileX * 8 + inX; \
612 BACKGROUND_TEXT_SELECT_CHARACTER; \
613 localY = inY & 0x7; \
614 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
615 localY = 7 - localY; \
616 } \
617 int mod8 = length & 0x7; \
618 if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \
619 mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw!"); \
620 return; \
621 } \
622 DRAW_BACKGROUND_MODE_0_TILE_PREFIX_ ## BPP (BLEND, OBJWIN) \
623 } \
624 if (VIDEO_CHECKS && UNLIKELY(outX > renderer->masterEnd)) { \
625 mLOG(GBA_VIDEO, FATAL, "Out of bounds background draw occurred!"); \
626 return; \
627 }
628
629void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
630 int inX = (renderer->start + background->x - background->offsetX) & 0x1FF;
631 int length = renderer->end - renderer->start;
632 if (background->mosaic) {
633 int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1;
634 y -= y % mosaicV;
635 }
636 int inY = y + background->y - background->offsetY;
637 uint16_t mapData;
638
639 unsigned yBase = inY & 0xF8;
640 if (background->size == 2) {
641 yBase += inY & 0x100;
642 } else if (background->size == 3) {
643 yBase += (inY & 0x100) << 1;
644 }
645 yBase <<= 3;
646
647 int localX;
648 int localY;
649
650 unsigned xBase;
651
652 uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND;
653 flags |= FLAG_TARGET_2 * background->target2;
654 int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed));
655 objwinFlags |= flags;
656 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed));
657 if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) {
658 flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
659 objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2);
660 }
661
662 uint32_t screenBase;
663 uint32_t charBase;
664 int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
665 color_t* mainPalette = renderer->normalPalette;
666 if (background->multipalette && background->extPalette) {
667 mainPalette = background->extPalette;
668 if (variant) {
669 mainPalette = background->variantPalette;
670 }
671 } else {
672 if (renderer->d.highlightAmount && background->highlight) {
673 mainPalette = renderer->highlightPalette;
674 }
675 if (variant) {
676 mainPalette = renderer->variantPalette;
677 if (renderer->d.highlightAmount && background->highlight) {
678 mainPalette = renderer->highlightVariantPalette;
679 }
680 }
681 }
682 color_t* palette = mainPalette;
683 PREPARE_OBJWIN;
684
685 int outX = renderer->start;
686
687 uint32_t tileData;
688 uint32_t current;
689 int pixelData;
690 int paletteData;
691 int tileX;
692 int tileEnd = ((length + inX) >> 3) - (inX >> 3);
693 uint16_t* vram = NULL;
694
695 if (background->yCache != inY >> 3) {
696 localX = 0;
697 for (tileX = 0; tileX < 64; ++tileX, localX += 8) {
698 BACKGROUND_TEXT_SELECT_CHARACTER;
699 background->mapCache[tileX] = mapData;
700 }
701 background->yCache = inY >> 3;
702 }
703
704 tileX = 0;
705 if (!objwinSlowPath) {
706 if (!(flags & FLAG_TARGET_2)) {
707 if (!background->multipalette) {
708 DRAW_BACKGROUND_MODE_0(16, NoBlend, NO_OBJWIN);
709 } else if (!background->extPalette) {
710 DRAW_BACKGROUND_MODE_0(256, NoBlend, NO_OBJWIN);
711 } else {
712 DRAW_BACKGROUND_MODE_0(256EXT, NoBlend, NO_OBJWIN);
713 }
714 } else {
715 if (!background->multipalette) {
716 DRAW_BACKGROUND_MODE_0(16, Blend, NO_OBJWIN);
717 } else if (!background->extPalette) {
718 DRAW_BACKGROUND_MODE_0(256, Blend, NO_OBJWIN);
719 } else {
720 DRAW_BACKGROUND_MODE_0(256EXT, Blend, NO_OBJWIN);
721 }
722 }
723 } else {
724 if (!(flags & FLAG_TARGET_2)) {
725 if (!background->multipalette) {
726 DRAW_BACKGROUND_MODE_0(16, NoBlend, OBJWIN);
727 } else if (!background->extPalette) {
728 DRAW_BACKGROUND_MODE_0(256, NoBlend, OBJWIN);
729 } else {
730 DRAW_BACKGROUND_MODE_0(256EXT, NoBlend, OBJWIN);
731 }
732 } else {
733 if (!background->multipalette) {
734 DRAW_BACKGROUND_MODE_0(16, Blend, OBJWIN);
735 } else if (!background->extPalette) {
736 DRAW_BACKGROUND_MODE_0(256, Blend, OBJWIN);
737 } else {
738 DRAW_BACKGROUND_MODE_0(256EXT, Blend, OBJWIN);
739 }
740 }
741 }
742}