src/gba/renderers/video-software.c (view raw)
1#include "video-software.h"
2
3#include "gba.h"
4#include "gba-io.h"
5
6#include <string.h>
7
8static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer);
9static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer);
10static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
11static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
12static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
13static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
14static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer);
15
16static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer);
17static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value);
18static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value);
19static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value);
20static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value);
21static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value);
22static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
23static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
24static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
25static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value);
26static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value);
27
28static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y);
29static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
30static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
31static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
32static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
33static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y);
34static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y);
35static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y);
36static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority);
37
38static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
39static inline uint32_t _brighten(uint32_t color, int y);
40static inline uint32_t _darken(uint32_t color, int y);
41static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB);
42
43void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) {
44 renderer->d.init = GBAVideoSoftwareRendererInit;
45 renderer->d.deinit = GBAVideoSoftwareRendererDeinit;
46 renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister;
47 renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM;
48 renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette;
49 renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline;
50 renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame;
51
52 renderer->d.turbo = 0;
53 renderer->d.framesPending = 0;
54 renderer->d.frameskip = 0;
55
56 {
57 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
58 renderer->mutex = mutex;
59 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
60 renderer->upCond = cond;
61 renderer->downCond = cond;
62 }
63}
64
65static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) {
66 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
67 int i;
68
69 softwareRenderer->dispcnt.packed = 0x0080;
70
71 softwareRenderer->target1Obj = 0;
72 softwareRenderer->target1Bd = 0;
73 softwareRenderer->target2Obj = 0;
74 softwareRenderer->target2Bd = 0;
75 softwareRenderer->blendEffect = BLEND_NONE;
76 memset(softwareRenderer->normalPalette, 0, sizeof(softwareRenderer->normalPalette));
77 memset(softwareRenderer->variantPalette, 0, sizeof(softwareRenderer->variantPalette));
78 memset(softwareRenderer->enabledBitmap, 0, sizeof(softwareRenderer->enabledBitmap));
79
80 softwareRenderer->blda = 0;
81 softwareRenderer->bldb = 0;
82 softwareRenderer->bldy = 0;
83
84 for (i = 0; i < 4; ++i) {
85 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
86 bg->index = i;
87 bg->enabled = 0;
88 bg->priority = 0;
89 bg->charBase = 0;
90 bg->mosaic = 0;
91 bg->multipalette = 0;
92 bg->screenBase = 0;
93 bg->overflow = 0;
94 bg->size = 0;
95 bg->target1 = 0;
96 bg->target2 = 0;
97 bg->x = 0;
98 bg->y = 0;
99 bg->refx = 0;
100 bg->refy = 0;
101 bg->dx = 256;
102 bg->dmx = 0;
103 bg->dy = 0;
104 bg->dmy = 256;
105 bg->sx = 0;
106 bg->sy = 0;
107 }
108
109 pthread_mutex_init(&softwareRenderer->mutex, 0);
110 pthread_cond_init(&softwareRenderer->upCond, 0);
111 pthread_cond_init(&softwareRenderer->downCond, 0);
112}
113
114static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
115 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
116
117 pthread_mutex_lock(&softwareRenderer->mutex);
118 pthread_cond_broadcast(&softwareRenderer->upCond);
119 pthread_mutex_unlock(&softwareRenderer->mutex);
120
121 pthread_mutex_destroy(&softwareRenderer->mutex);
122 pthread_cond_destroy(&softwareRenderer->upCond);
123 pthread_cond_destroy(&softwareRenderer->downCond);
124}
125
126static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
127 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
128 switch (address) {
129 case REG_DISPCNT:
130 softwareRenderer->dispcnt.packed = value;
131 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
132 break;
133 case REG_BG0CNT:
134 value &= 0xFFCF;
135 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
136 break;
137 case REG_BG1CNT:
138 value &= 0xFFCF;
139 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
140 break;
141 case REG_BG2CNT:
142 value &= 0xFFCF;
143 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
144 break;
145 case REG_BG3CNT:
146 value &= 0xFFCF;
147 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
148 break;
149 case REG_BG0HOFS:
150 value &= 0x01FF;
151 softwareRenderer->bg[0].x = value;
152 break;
153 case REG_BG0VOFS:
154 value &= 0x01FF;
155 softwareRenderer->bg[0].y = value;
156 break;
157 case REG_BG1HOFS:
158 value &= 0x01FF;
159 softwareRenderer->bg[1].x = value;
160 break;
161 case REG_BG1VOFS:
162 value &= 0x01FF;
163 softwareRenderer->bg[1].y = value;
164 break;
165 case REG_BG2HOFS:
166 value &= 0x01FF;
167 softwareRenderer->bg[2].x = value;
168 break;
169 case REG_BG2VOFS:
170 value &= 0x01FF;
171 softwareRenderer->bg[2].y = value;
172 break;
173 case REG_BG3HOFS:
174 value &= 0x01FF;
175 softwareRenderer->bg[3].x = value;
176 break;
177 case REG_BG3VOFS:
178 value &= 0x01FF;
179 softwareRenderer->bg[3].y = value;
180 break;
181 case REG_BG2PA:
182 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
183 break;
184 case REG_BG2PB:
185 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
186 break;
187 case REG_BG2PC:
188 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
189 break;
190 case REG_BG2PD:
191 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
192 break;
193 case REG_BG2X_LO:
194 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
195 break;
196 case REG_BG2X_HI:
197 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
198 break;
199 case REG_BG2Y_LO:
200 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
201 break;
202 case REG_BG2Y_HI:
203 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
204 break;
205 case REG_BG3PA:
206 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
207 break;
208 case REG_BG3PB:
209 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
210 break;
211 case REG_BG3PC:
212 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
213 break;
214 case REG_BG3PD:
215 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
216 break;
217 case REG_BG3X_LO:
218 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
219 break;
220 case REG_BG3X_HI:
221 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
222 break;
223 case REG_BG3Y_LO:
224 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
225 break;
226 case REG_BG3Y_HI:
227 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
228 break;
229 case REG_BLDCNT:
230 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
231 break;
232 case REG_BLDALPHA:
233 softwareRenderer->blda = value & 0x1F;
234 if (softwareRenderer->blda > 0x10) {
235 softwareRenderer->blda = 0x10;
236 }
237 softwareRenderer->bldb = (value >> 8) & 0x1F;
238 if (softwareRenderer->bldb > 0x10) {
239 softwareRenderer->bldb = 0x10;
240 }
241 break;
242 case REG_BLDY:
243 softwareRenderer->bldy = value & 0x1F;
244 if (softwareRenderer->bldy > 0x10) {
245 softwareRenderer->bldy = 0x10;
246 }
247 _updatePalettes(softwareRenderer);
248 break;
249 default:
250 GBALog(GBA_LOG_STUB, "Stub video register write: %03x", address);
251 }
252 return value;
253}
254
255static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
256 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
257 if ((oam & 0x3) != 0x3) {
258 oam >>= 2;
259 struct GBAObj* sprite = &renderer->oam->obj[oam];
260 int enabled = sprite->transformed || !sprite->disable;
261 enabled <<= (oam & 0x1F);
262 softwareRenderer->enabledBitmap[oam >> 5] = (softwareRenderer->enabledBitmap[oam >> 5] & ~(1 << (oam & 0x1F))) | enabled;
263 }
264}
265
266static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
267 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
268 uint32_t color32 = 0;
269 color32 |= (value << 3) & 0xF8;
270 color32 |= (value << 6) & 0xF800;
271 color32 |= (value << 9) & 0xF80000;
272 softwareRenderer->normalPalette[address >> 1] = color32;
273 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
274 softwareRenderer->variantPalette[address >> 1] = _brighten(color32, softwareRenderer->bldy);
275 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
276 softwareRenderer->variantPalette[address >> 1] = _darken(color32, softwareRenderer->bldy);
277 }
278}
279
280static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
281 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
282 if (renderer->frameskip > 0) {
283 return;
284 }
285 uint32_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
286 if (softwareRenderer->dispcnt.forcedBlank) {
287 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
288 row[x] = GBA_COLOR_WHITE;
289 }
290 return;
291 } else {
292 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
293 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA) {
294 backdrop |= softwareRenderer->normalPalette[0];
295 } else {
296 backdrop |= softwareRenderer->variantPalette[0];
297 }
298 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
299 row[x] = backdrop;
300 }
301 }
302
303 softwareRenderer->row = row;
304
305 softwareRenderer->start = 0;
306 softwareRenderer->end = VIDEO_HORIZONTAL_PIXELS;
307 _drawScanline(softwareRenderer, y);
308}
309
310static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
311 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
312
313 pthread_mutex_lock(&softwareRenderer->mutex);
314 if (renderer->frameskip > 0) {
315 --renderer->frameskip;
316 } else {
317 renderer->framesPending++;
318 pthread_cond_broadcast(&softwareRenderer->upCond);
319 if (!renderer->turbo) {
320 pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
321 }
322 }
323 pthread_mutex_unlock(&softwareRenderer->mutex);
324
325 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
326 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
327 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
328 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
329}
330
331static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
332 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
333 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
334 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
335 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
336}
337
338static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
339 (void)(renderer);
340 union GBARegisterBGCNT reg = { .packed = value };
341 bg->priority = reg.priority;
342 bg->charBase = reg.charBase << 14;
343 bg->mosaic = reg.mosaic;
344 bg->multipalette = reg.multipalette;
345 bg->screenBase = reg.screenBase << 11;
346 bg->overflow = reg.overflow;
347 bg->size = reg.size;
348}
349
350static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
351 bg->dx = value;
352}
353
354static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
355 bg->dmx = value;
356}
357
358static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
359 bg->dy = value;
360}
361
362static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
363 bg->dmy = value;
364}
365
366static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
367 bg->refx = (bg->refx & 0xFFFF0000) | value;
368 bg->sx = bg->refx;
369}
370
371static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
372 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
373 bg->refx <<= 4;
374 bg->refx >>= 4;
375 bg->sx = bg->refx;
376}
377
378static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
379 bg->refy = (bg->refy & 0xFFFF0000) | value;
380 bg->sy = bg->refy;
381}
382
383static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
384 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
385 bg->refy <<= 4;
386 bg->refy >>= 4;
387 bg->sy = bg->refy;
388}
389
390static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
391 union {
392 struct {
393 unsigned target1Bg0 : 1;
394 unsigned target1Bg1 : 1;
395 unsigned target1Bg2 : 1;
396 unsigned target1Bg3 : 1;
397 unsigned target1Obj : 1;
398 unsigned target1Bd : 1;
399 enum BlendEffect effect : 2;
400 unsigned target2Bg0 : 1;
401 unsigned target2Bg1 : 1;
402 unsigned target2Bg2 : 1;
403 unsigned target2Bg3 : 1;
404 unsigned target2Obj : 1;
405 unsigned target2Bd : 1;
406 };
407 uint16_t packed;
408 } bldcnt = { .packed = value };
409
410 enum BlendEffect oldEffect = renderer->blendEffect;
411
412 renderer->bg[0].target1 = bldcnt.target1Bg0;
413 renderer->bg[1].target1 = bldcnt.target1Bg1;
414 renderer->bg[2].target1 = bldcnt.target1Bg2;
415 renderer->bg[3].target1 = bldcnt.target1Bg3;
416 renderer->bg[0].target2 = bldcnt.target2Bg0;
417 renderer->bg[1].target2 = bldcnt.target2Bg1;
418 renderer->bg[2].target2 = bldcnt.target2Bg2;
419 renderer->bg[3].target2 = bldcnt.target2Bg3;
420
421 renderer->blendEffect = bldcnt.effect;
422 renderer->target1Obj = bldcnt.target1Obj;
423 renderer->target1Bd = bldcnt.target1Bd;
424 renderer->target2Obj = bldcnt.target2Obj;
425 renderer->target2Bd = bldcnt.target2Bd;
426
427 if (oldEffect != renderer->blendEffect) {
428 _updatePalettes(renderer);
429 }
430}
431
432static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
433 int i;
434 memset(renderer->spriteLayer, 0, sizeof(renderer->spriteLayer));
435 if (renderer->dispcnt.objEnable) {
436 int j;
437 for (j = 0; j < 4; ++j) {
438 uint32_t bitmap = renderer->enabledBitmap[j];
439 if (!bitmap) {
440 continue;
441 }
442 for (i = j * 32; i < (j + 1) * 32; ++i) {
443 if (bitmap & 1) {
444 struct GBAObj* sprite = &renderer->d.oam->obj[i];
445 if (sprite->transformed) {
446 _preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
447 } else {
448 _preprocessSprite(renderer, sprite, y);
449 }
450 }
451 bitmap >>= 1;
452 }
453 }
454 }
455
456 int priority;
457 for (priority = 0; priority < 4; ++priority) {
458 _postprocessSprite(renderer, priority);
459 if (renderer->bg[0].enabled && renderer->bg[0].priority == priority && renderer->dispcnt.mode < 2) {
460 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
461 }
462 if (renderer->bg[1].enabled && renderer->bg[1].priority == priority && renderer->dispcnt.mode < 2) {
463 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
464 }
465 if (renderer->bg[2].enabled && renderer->bg[2].priority == priority) {
466 switch (renderer->dispcnt.mode) {
467 case 0:
468 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
469 break;
470 case 1:
471 case 2:
472 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
473 break;
474 case 3:
475 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
476 break;
477 case 4:
478 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
479 break;
480 case 5:
481 _drawBackgroundMode5(renderer, &renderer->bg[2], y);
482 break;
483 }
484 renderer->bg[2].sx += renderer->bg[2].dmx;
485 renderer->bg[2].sy += renderer->bg[2].dmy;
486 }
487 if (renderer->bg[3].enabled && renderer->bg[3].priority == priority) {
488 switch (renderer->dispcnt.mode) {
489 case 0:
490 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
491 break;
492 case 2:
493 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
494 break;
495 }
496 renderer->bg[3].sx += renderer->bg[3].dmx;
497 renderer->bg[3].sy += renderer->bg[3].dmy;
498 }
499 }
500}
501
502static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color) {
503 uint32_t current = renderer->row[offset];
504 // We stash the priority on the top bits so we cn do a one-operator comparison
505 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
506 // We want to do special processing if the color pixel is target 1, however
507 if (color < current) {
508 if (current & FLAG_UNWRITTEN) {
509 renderer->row[offset] = color;
510 } else if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
511 renderer->row[offset] = color | FLAG_FINALIZED;
512 } else {
513 renderer->row[offset] = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
514 }
515 } else {
516 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
517 renderer->row[offset] = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
518 } else {
519 renderer->row[offset] = current | FLAG_FINALIZED;
520 }
521 }
522}
523
524#define BACKGROUND_DRAW_PIXEL_16_NORMAL \
525 pixelData = tileData & 0xF; \
526 if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
527 _composite(renderer, outX, renderer->normalPalette[pixelData | paletteData] | flags); \
528 } \
529 tileData >>= 4;
530
531#define BACKGROUND_DRAW_PIXEL_16_VARIANT \
532 pixelData = tileData & 0xF; \
533 if (tileData & 0xF && !(renderer->row[outX] & FLAG_FINALIZED)) { \
534 _composite(renderer, outX, renderer->variantPalette[pixelData | paletteData] | flags); \
535 } \
536 tileData >>= 4;
537
538#define BACKGROUND_DRAW_PIXEL_256_NORMAL \
539 pixelData = tileData & 0xFF; \
540 if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
541 _composite(renderer, outX, renderer->normalPalette[pixelData] | flags); \
542 } \
543 tileData >>= 8;
544
545#define BACKGROUND_DRAW_PIXEL_256_VARIANT \
546 pixelData = tileData & 0xFF; \
547 if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
548 _composite(renderer, outX, renderer->variantPalette[pixelData] | flags); \
549 } \
550 tileData >>= 8;
551
552#define BACKGROUND_TEXT_SELECT_CHARACTER \
553 localX = tileX * 8 + inX; \
554 xBase = localX & 0xF8; \
555 if (background->size & 1) { \
556 xBase += (localX & 0x100) << 5; \
557 } \
558 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
559 mapData.packed = renderer->d.vram[screenBase]; \
560 if (!mapData.vflip) { \
561 localY = inY & 0x7; \
562 } else { \
563 localY = 7 - (inY & 0x7); \
564 }
565
566#define BACKGROUND_MODE_0_TILE_16_LOOP(TYPE) \
567 uint32_t tileData; \
568 int paletteData, pixelData; \
569 for (; tileX < 30; ++tileX) { \
570 BACKGROUND_TEXT_SELECT_CHARACTER; \
571 paletteData = mapData.palette << 4; \
572 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY; \
573 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
574 if (tileData) { \
575 if (!mapData.hflip) { \
576 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
577 ++outX; \
578 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
579 ++outX; \
580 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
581 ++outX; \
582 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
583 ++outX; \
584 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
585 ++outX; \
586 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
587 ++outX; \
588 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
589 ++outX; \
590 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
591 ++outX; \
592 } else { \
593 outX += 7; \
594 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
595 --outX; \
596 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
597 --outX; \
598 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
599 --outX; \
600 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
601 --outX; \
602 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
603 --outX; \
604 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
605 --outX; \
606 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
607 --outX; \
608 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
609 outX += 8; \
610 } \
611 } else { \
612 outX += 8; \
613 } \
614 }
615
616#define BACKGROUND_MODE_0_TILE_256_LOOP(TYPE) \
617 uint32_t tileData; \
618 int pixelData; \
619 for (; tileX < 30; ++tileX) { \
620 BACKGROUND_TEXT_SELECT_CHARACTER; \
621 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1); \
622 if (!mapData.hflip) { \
623 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
624 if (tileData) { \
625 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
626 ++outX; \
627 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
628 ++outX; \
629 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
630 ++outX; \
631 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
632 ++outX; \
633 } else { \
634 outX += 4; \
635 } \
636 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
637 if (tileData) { \
638 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
639 ++outX; \
640 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
641 ++outX; \
642 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
643 ++outX; \
644 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
645 ++outX; \
646 } else { \
647 outX += 4; \
648 } \
649 } else { \
650 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
651 if (tileData) { \
652 outX += 3; \
653 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
654 --outX; \
655 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
656 --outX; \
657 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
658 --outX; \
659 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
660 outX += 4; \
661 } else { \
662 outX += 4; \
663 } \
664 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
665 if (tileData) { \
666 outX += 3; \
667 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
668 --outX; \
669 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
670 --outX; \
671 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
672 --outX; \
673 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
674 outX += 4; \
675 } else { \
676 outX += 4; \
677 } \
678 } \
679 }
680
681static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
682 int inX = background->x;
683 int inY = y + background->y;
684 union GBATextMapData mapData;
685
686 unsigned yBase = inY & 0xF8;
687 if (background->size == 2) {
688 yBase += inY & 0x100;
689 } else if (background->size == 3) {
690 yBase += (inY & 0x100) << 1;
691 }
692
693 int localX;
694 int localY;
695
696 unsigned xBase;
697
698 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
699 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
700 flags |= FLAG_TARGET_2 * background->target2;
701
702 uint32_t screenBase;
703 uint32_t charBase;
704 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
705
706 int outX = 0;
707 int tileX = 0;
708 if (inX & 0x7) {
709 uint32_t tileData;
710 int pixelData, paletteData;
711 BACKGROUND_TEXT_SELECT_CHARACTER;
712
713 int end = 0x8 - (inX & 0x7);
714 if (!background->multipalette) {
715 paletteData = mapData.palette << 4;
716 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
717 tileData = ((uint32_t*)renderer->d.vram)[charBase];
718 if (!mapData.hflip) {
719 tileData >>= 4 * (inX & 0x7);
720 if (!variant) {
721 for (outX = 0; outX < end; ++outX) {
722 BACKGROUND_DRAW_PIXEL_16_NORMAL;
723 }
724 } else {
725 for (outX = 0; outX < end; ++outX) {
726 BACKGROUND_DRAW_PIXEL_16_VARIANT;
727 }
728 }
729 } else {
730 if (!variant) {
731 for (outX = end; outX--;) {
732 BACKGROUND_DRAW_PIXEL_16_NORMAL;
733 }
734 } else {
735 for (outX = end; outX--;) {
736 BACKGROUND_DRAW_PIXEL_16_VARIANT;
737 }
738 }
739 }
740 } else {
741 // TODO: hflip
742 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
743 outX = 0;
744 int end2 = end - 4;
745 int shift = inX & 0x3;
746 if (end2 > 0) {
747 tileData = ((uint32_t*)renderer->d.vram)[charBase];
748 tileData >>= 8 * shift;
749 shift = 0;
750 if (!variant) {
751 for (; outX < end2; ++outX) {
752 BACKGROUND_DRAW_PIXEL_256_NORMAL;
753 }
754 } else {
755 for (; outX < end2; ++outX) {
756 BACKGROUND_DRAW_PIXEL_256_VARIANT;
757 }
758 }
759 }
760
761 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
762 tileData >>= 8 * shift;
763 if (!variant) {
764 for (; outX < end; ++outX) {
765 BACKGROUND_DRAW_PIXEL_256_NORMAL;
766 }
767 } else {
768 for (; outX < end; ++outX) {
769 BACKGROUND_DRAW_PIXEL_256_VARIANT;
770 }
771 }
772 }
773
774 tileX = 30;
775 BACKGROUND_TEXT_SELECT_CHARACTER;
776 if (!background->multipalette) {
777 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
778 tileData = ((uint32_t*)renderer->d.vram)[charBase];
779 paletteData = mapData.palette << 4;
780 if (!mapData.hflip) {
781 if (!variant) {
782 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
783 BACKGROUND_DRAW_PIXEL_16_NORMAL;
784 }
785 } else {
786 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
787 BACKGROUND_DRAW_PIXEL_16_VARIANT;
788 }
789 }
790 } else {
791 tileData >>= 4 * end;
792 if (!variant) {
793 for (outX = VIDEO_HORIZONTAL_PIXELS - 1; outX > VIDEO_HORIZONTAL_PIXELS - 8; --outX) {
794 BACKGROUND_DRAW_PIXEL_16_NORMAL;
795 }
796 } else {
797 for (outX = VIDEO_HORIZONTAL_PIXELS - 1; outX > VIDEO_HORIZONTAL_PIXELS - 8; --outX) {
798 BACKGROUND_DRAW_PIXEL_16_VARIANT;
799 }
800 }
801 }
802 } else {
803 // TODO: hflip
804 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
805 outX = VIDEO_HORIZONTAL_PIXELS - 8 + end;
806 int end2 = 4 - end;
807 if (end2 > 0) {
808 tileData = ((uint32_t*)renderer->d.vram)[charBase];
809 if (!variant) {
810 for (; outX < VIDEO_HORIZONTAL_PIXELS - end2; ++outX) {
811 BACKGROUND_DRAW_PIXEL_256_NORMAL;
812 }
813 } else {
814 for (; outX < VIDEO_HORIZONTAL_PIXELS - end2; ++outX) {
815 BACKGROUND_DRAW_PIXEL_256_VARIANT;
816 }
817 }
818 ++charBase;
819 }
820
821 tileData = ((uint32_t*)renderer->d.vram)[charBase];
822 if (!variant) {
823 for (; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
824 BACKGROUND_DRAW_PIXEL_256_NORMAL;
825 }
826 } else {
827 for (; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
828 BACKGROUND_DRAW_PIXEL_256_VARIANT;
829 }
830 }
831 }
832
833 tileX = 1;
834 outX = end;
835 }
836
837 if (!background->multipalette) {
838 if (!variant) {
839 BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
840 } else {
841 BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
842 }
843 } else {
844 if (!variant) {
845 BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
846 } else {
847 BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
848 }
849 }
850}
851
852#define BACKGROUND_BITMAP_INIT \
853 (void)(unused); \
854 int32_t x = background->sx - background->dx; \
855 int32_t y = background->sy - background->dy; \
856 int32_t localX; \
857 int32_t localY; \
858 \
859 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
860 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
861 flags |= FLAG_TARGET_2 * background->target2; \
862 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
863
864#define BACKGROUND_BITMAP_ITERATE(W, H) \
865 x += background->dx; \
866 y += background->dy; \
867 \
868 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
869 continue; \
870 } else { \
871 localX = x; \
872 localY = y; \
873 }
874
875static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
876 int sizeAdjusted = 0x8000 << background->size;
877
878 BACKGROUND_BITMAP_INIT;
879
880 uint32_t screenBase = background->screenBase;
881 uint32_t charBase = background->charBase;
882 uint8_t mapData;
883 uint8_t tileData;
884
885 int outX;
886 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
887 x += background->dx;
888 y += background->dy;
889
890 if (background->overflow) {
891 localX = x & (sizeAdjusted - 1);
892 localY = y & (sizeAdjusted - 1);
893 } else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
894 continue;
895 } else {
896 localX = x;
897 localY = y;
898 }
899 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
900 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
901
902 if (tileData && !(renderer->row[outX] & FLAG_FINALIZED)) {
903 if (!variant) {
904 _composite(renderer, outX, renderer->normalPalette[tileData] | flags);
905 } else {
906 _composite(renderer, outX, renderer->variantPalette[tileData] | flags);
907 }
908 }
909 }
910}
911
912static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
913 BACKGROUND_BITMAP_INIT;
914
915 uint16_t color;
916 uint32_t color32;
917
918 int outX;
919 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
920 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
921
922 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
923 color32 = 0;
924 color32 |= (color << 3) & 0xF8;
925 color32 |= (color << 6) & 0xF800;
926 color32 |= (color << 9) & 0xF80000;
927
928 if (!(renderer->row[outX] & FLAG_FINALIZED)) {
929 if (!variant) {
930 _composite(renderer, outX, color32 | flags);
931 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
932 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
933 } else if (renderer->blendEffect == BLEND_DARKEN) {
934 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
935 }
936 }
937 }
938}
939
940static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
941 BACKGROUND_BITMAP_INIT;
942
943 uint16_t color;
944 uint32_t offset = 0;
945 if (renderer->dispcnt.frameSelect) {
946 offset = 0xA000;
947 }
948
949 int outX;
950 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
951 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
952
953 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
954
955 if (color && !(renderer->row[outX] & FLAG_FINALIZED)) {
956 if (!variant) {
957 _composite(renderer, outX, renderer->normalPalette[color] | flags);
958 } else {
959 _composite(renderer, outX, renderer->variantPalette[color] | flags);
960 }
961 }
962 }
963}
964
965static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
966 BACKGROUND_BITMAP_INIT;
967
968 uint16_t color;
969 uint32_t color32;
970 uint32_t offset = 0;
971 if (renderer->dispcnt.frameSelect) {
972 offset = 0xA000;
973 }
974
975 int outX;
976 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
977 BACKGROUND_BITMAP_ITERATE(160, 128);
978
979 color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
980 color32 = 0;
981 color32 |= (color << 3) & 0xF8;
982 color32 |= (color << 6) & 0xF800;
983 color32 |= (color << 9) & 0xF80000;
984
985 if (!(renderer->row[outX] & FLAG_FINALIZED)) {
986 if (!variant) {
987 _composite(renderer, outX, color32 | flags);
988 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
989 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
990 } else if (renderer->blendEffect == BLEND_DARKEN) {
991 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
992 }
993 }
994 }
995}
996
997static const int _objSizes[32] = {
998 8, 8,
999 16, 16,
1000 32, 32,
1001 64, 64,
1002 16, 8,
1003 32, 8,
1004 32, 16,
1005 64, 32,
1006 8, 16,
1007 8, 32,
1008 16, 32,
1009 32, 64,
1010 0, 0,
1011 0, 0,
1012 0, 0,
1013 0, 0
1014};
1015
1016#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1017 SPRITE_YBASE_ ## DEPTH(inY); \
1018 for (int outX = x >= start ? x : start; outX < x + width && outX < end; ++outX) { \
1019 int inX = outX - x; \
1020 if (sprite->hflip) { \
1021 inX = width - inX - 1; \
1022 } \
1023 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1024 continue; \
1025 } \
1026 SPRITE_XBASE_ ## DEPTH(inX); \
1027 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1028 }
1029
1030#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1031 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1032 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1033 continue; \
1034 } \
1035 int inX = outX - x; \
1036 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1037 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1038 \
1039 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1040 continue; \
1041 } \
1042 \
1043 SPRITE_YBASE_ ## DEPTH(localY); \
1044 SPRITE_XBASE_ ## DEPTH(localX); \
1045 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1046 }
1047
1048#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1049#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1050
1051#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1052 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1053 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1054 if (tileData && !(renderer->spriteLayer[outX])) { \
1055 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1056 }
1057
1058#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1059 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1060 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1061 if (tileData && !(renderer->spriteLayer[outX])) { \
1062 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1063 }
1064
1065#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1066#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1067
1068#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1069 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1070 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1071 if (tileData && !(renderer->spriteLayer[outX])) { \
1072 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1073 }
1074
1075#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1076 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1077 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1078 if (tileData && !(renderer->spriteLayer[outX])) { \
1079 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1080 }
1081
1082static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1083 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1084 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1085 int start = renderer->start;
1086 int end = renderer->end;
1087 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1088 return;
1089 }
1090 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1091 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1092 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1093 int x = sprite->x;
1094 int inY = y - sprite->y;
1095 if (sprite->y + height - 256 >= 0) {
1096 inY += 256;
1097 }
1098 if (sprite->vflip) {
1099 inY = height - inY - 1;
1100 }
1101 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1102 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1103 if (!sprite->multipalette) {
1104 if (!variant) {
1105 SPRITE_NORMAL_LOOP(16, NORMAL);
1106 } else {
1107 SPRITE_NORMAL_LOOP(16, VARIANT);
1108 }
1109 } else {
1110 if (!variant) {
1111 SPRITE_NORMAL_LOOP(256, NORMAL);
1112 } else {
1113 SPRITE_NORMAL_LOOP(256, VARIANT);
1114 }
1115 }
1116}
1117
1118static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1119 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1120 int totalWidth = width << sprite->doublesize;
1121 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1122 int totalHeight = height << sprite->doublesize;
1123 int start = renderer->start;
1124 int end = renderer->end;
1125 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1126 return;
1127 }
1128 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1129 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1130 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1131 int x = sprite->x;
1132 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1133 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1134 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1135 int inY = y - sprite->y;
1136 if (inY < 0) {
1137 inY += 256;
1138 }
1139 if (!sprite->multipalette) {
1140 if (!variant) {
1141 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1142 } else {
1143 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1144 }
1145 } else {
1146 if (!variant) {
1147 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1148 } else {
1149 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1150 }
1151 }
1152}
1153
1154static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority) {
1155 int x;
1156 for (x = 0; x < 240; ++x) {
1157 uint32_t color = renderer->spriteLayer[x];
1158 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(renderer->row[x] & FLAG_FINALIZED)) {
1159 _composite(renderer, x, color & ~FLAG_FINALIZED);
1160 }
1161 }
1162}
1163
1164static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1165 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1166 for (int i = 0; i < 512; ++i) {
1167 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1168 }
1169 } else if (renderer->blendEffect == BLEND_DARKEN) {
1170 for (int i = 0; i < 512; ++i) {
1171 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1172 }
1173 } else {
1174 for (int i = 0; i < 512; ++i) {
1175 renderer->variantPalette[i] = renderer->normalPalette[i];
1176 }
1177 }
1178}
1179
1180static inline uint32_t _brighten(uint32_t color, int y) {
1181 uint32_t c = 0;
1182 uint32_t a;
1183 a = color & 0xF8;
1184 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1185
1186 a = color & 0xF800;
1187 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1188
1189 a = color & 0xF80000;
1190 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1191 return c;
1192}
1193
1194static inline uint32_t _darken(uint32_t color, int y) {
1195 uint32_t c = 0;
1196 uint32_t a;
1197 a = color & 0xF8;
1198 c |= (a - (a * y) / 16) & 0xF8;
1199
1200 a = color & 0xF800;
1201 c |= (a - (a * y) / 16) & 0xF800;
1202
1203 a = color & 0xF80000;
1204 c |= (a - (a * y) / 16) & 0xF80000;
1205 return c;
1206}
1207
1208static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
1209 uint32_t c = 0;
1210 uint32_t a, b;
1211 a = colorA & 0xF8;
1212 b = colorB & 0xF8;
1213 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1214 if (c & 0x00000100) {
1215 c = 0x000000F8;
1216 }
1217
1218 a = colorA & 0xF800;
1219 b = colorB & 0xF800;
1220 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1221 if (c & 0x00010000) {
1222 c = (c & 0x000000F8) | 0x0000F800;
1223 }
1224
1225 a = colorA & 0xF80000;
1226 b = colorB & 0xF80000;
1227 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1228 if (c & 0x01000000) {
1229 c = (c & 0x0000F8F8) | 0x00F80000;
1230 }
1231 return c;
1232}