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 case REG_WIN0H:
250 softwareRenderer->win0H.packed = value;
251 break;
252 case REG_WIN1H:
253 softwareRenderer->win1H.packed = value;
254 break;
255 case REG_WIN0V:
256 softwareRenderer->win0V.packed = value;
257 break;
258 case REG_WIN1V:
259 softwareRenderer->win1V.packed = value;
260 break;
261 case REG_WININ:
262 softwareRenderer->winin.packed = value;
263 break;
264 case REG_WINOUT:
265 softwareRenderer->winout.packed = value;
266 break;
267 default:
268 GBALog(GBA_LOG_STUB, "Stub video register write: %03x", address);
269 }
270 return value;
271}
272
273static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
274 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
275 if ((oam & 0x3) != 0x3) {
276 oam >>= 2;
277 struct GBAObj* sprite = &renderer->oam->obj[oam];
278 int enabled = sprite->transformed || !sprite->disable;
279 enabled <<= (oam & 0x1F);
280 softwareRenderer->enabledBitmap[oam >> 5] = (softwareRenderer->enabledBitmap[oam >> 5] & ~(1 << (oam & 0x1F))) | enabled;
281 }
282}
283
284static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
285 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
286 uint32_t color32 = 0;
287 color32 |= (value << 3) & 0xF8;
288 color32 |= (value << 6) & 0xF800;
289 color32 |= (value << 9) & 0xF80000;
290 softwareRenderer->normalPalette[address >> 1] = color32;
291 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
292 softwareRenderer->variantPalette[address >> 1] = _brighten(color32, softwareRenderer->bldy);
293 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
294 softwareRenderer->variantPalette[address >> 1] = _darken(color32, softwareRenderer->bldy);
295 }
296}
297
298static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
299 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
300 if (renderer->frameskip > 0) {
301 return;
302 }
303 uint32_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
304 if (softwareRenderer->dispcnt.forcedBlank) {
305 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
306 row[x] = GBA_COLOR_WHITE;
307 }
308 return;
309 } else {
310 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
311 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA) {
312 backdrop |= softwareRenderer->normalPalette[0];
313 } else {
314 backdrop |= softwareRenderer->variantPalette[0];
315 }
316 for (int x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
317 row[x] = backdrop;
318 }
319 }
320
321 softwareRenderer->row = row;
322
323 softwareRenderer->start = 0;
324 softwareRenderer->end = VIDEO_HORIZONTAL_PIXELS;
325 _drawScanline(softwareRenderer, y);
326}
327
328static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
329 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
330
331 pthread_mutex_lock(&softwareRenderer->mutex);
332 if (renderer->frameskip > 0) {
333 --renderer->frameskip;
334 } else {
335 renderer->framesPending++;
336 pthread_cond_broadcast(&softwareRenderer->upCond);
337 if (!renderer->turbo) {
338 pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
339 }
340 }
341 pthread_mutex_unlock(&softwareRenderer->mutex);
342
343 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
344 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
345 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
346 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
347}
348
349static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
350 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
351 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
352 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
353 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
354}
355
356static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
357 (void)(renderer);
358 union GBARegisterBGCNT reg = { .packed = value };
359 bg->priority = reg.priority;
360 bg->charBase = reg.charBase << 14;
361 bg->mosaic = reg.mosaic;
362 bg->multipalette = reg.multipalette;
363 bg->screenBase = reg.screenBase << 11;
364 bg->overflow = reg.overflow;
365 bg->size = reg.size;
366}
367
368static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
369 bg->dx = value;
370}
371
372static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
373 bg->dmx = value;
374}
375
376static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
377 bg->dy = value;
378}
379
380static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
381 bg->dmy = value;
382}
383
384static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
385 bg->refx = (bg->refx & 0xFFFF0000) | value;
386 bg->sx = bg->refx;
387}
388
389static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
390 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
391 bg->refx <<= 4;
392 bg->refx >>= 4;
393 bg->sx = bg->refx;
394}
395
396static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
397 bg->refy = (bg->refy & 0xFFFF0000) | value;
398 bg->sy = bg->refy;
399}
400
401static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
402 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
403 bg->refy <<= 4;
404 bg->refy >>= 4;
405 bg->sy = bg->refy;
406}
407
408static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
409 union {
410 struct {
411 unsigned target1Bg0 : 1;
412 unsigned target1Bg1 : 1;
413 unsigned target1Bg2 : 1;
414 unsigned target1Bg3 : 1;
415 unsigned target1Obj : 1;
416 unsigned target1Bd : 1;
417 enum BlendEffect effect : 2;
418 unsigned target2Bg0 : 1;
419 unsigned target2Bg1 : 1;
420 unsigned target2Bg2 : 1;
421 unsigned target2Bg3 : 1;
422 unsigned target2Obj : 1;
423 unsigned target2Bd : 1;
424 };
425 uint16_t packed;
426 } bldcnt = { .packed = value };
427
428 enum BlendEffect oldEffect = renderer->blendEffect;
429
430 renderer->bg[0].target1 = bldcnt.target1Bg0;
431 renderer->bg[1].target1 = bldcnt.target1Bg1;
432 renderer->bg[2].target1 = bldcnt.target1Bg2;
433 renderer->bg[3].target1 = bldcnt.target1Bg3;
434 renderer->bg[0].target2 = bldcnt.target2Bg0;
435 renderer->bg[1].target2 = bldcnt.target2Bg1;
436 renderer->bg[2].target2 = bldcnt.target2Bg2;
437 renderer->bg[3].target2 = bldcnt.target2Bg3;
438
439 renderer->blendEffect = bldcnt.effect;
440 renderer->target1Obj = bldcnt.target1Obj;
441 renderer->target1Bd = bldcnt.target1Bd;
442 renderer->target2Obj = bldcnt.target2Obj;
443 renderer->target2Bd = bldcnt.target2Bd;
444
445 if (oldEffect != renderer->blendEffect) {
446 _updatePalettes(renderer);
447 }
448}
449
450static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
451 int i;
452 memset(renderer->spriteLayer, 0, sizeof(renderer->spriteLayer));
453 if (renderer->dispcnt.objEnable) {
454 int j;
455 for (j = 0; j < 4; ++j) {
456 uint32_t bitmap = renderer->enabledBitmap[j];
457 if (!bitmap) {
458 continue;
459 }
460 for (i = j * 32; i < (j + 1) * 32; ++i) {
461 if (bitmap & 1) {
462 struct GBAObj* sprite = &renderer->d.oam->obj[i];
463 if (sprite->transformed) {
464 _preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
465 } else {
466 _preprocessSprite(renderer, sprite, y);
467 }
468 }
469 bitmap >>= 1;
470 }
471 }
472 }
473
474 int priority;
475 for (priority = 0; priority < 4; ++priority) {
476 _postprocessSprite(renderer, priority);
477 if (renderer->bg[0].enabled && renderer->bg[0].priority == priority && renderer->dispcnt.mode < 2) {
478 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
479 }
480 if (renderer->bg[1].enabled && renderer->bg[1].priority == priority && renderer->dispcnt.mode < 2) {
481 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
482 }
483 if (renderer->bg[2].enabled && renderer->bg[2].priority == priority) {
484 switch (renderer->dispcnt.mode) {
485 case 0:
486 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
487 break;
488 case 1:
489 case 2:
490 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
491 break;
492 case 3:
493 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
494 break;
495 case 4:
496 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
497 break;
498 case 5:
499 _drawBackgroundMode5(renderer, &renderer->bg[2], y);
500 break;
501 }
502 renderer->bg[2].sx += renderer->bg[2].dmx;
503 renderer->bg[2].sy += renderer->bg[2].dmy;
504 }
505 if (renderer->bg[3].enabled && renderer->bg[3].priority == priority) {
506 switch (renderer->dispcnt.mode) {
507 case 0:
508 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
509 break;
510 case 2:
511 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
512 break;
513 }
514 renderer->bg[3].sx += renderer->bg[3].dmx;
515 renderer->bg[3].sy += renderer->bg[3].dmy;
516 }
517 }
518}
519
520static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color) {
521 uint32_t current = renderer->row[offset];
522 // We stash the priority on the top bits so we cn do a one-operator comparison
523 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
524 // We want to do special processing if the color pixel is target 1, however
525 if (color < current) {
526 if (current & FLAG_UNWRITTEN) {
527 renderer->row[offset] = color;
528 } else if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
529 renderer->row[offset] = color | FLAG_FINALIZED;
530 } else {
531 renderer->row[offset] = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
532 }
533 } else {
534 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
535 renderer->row[offset] = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
536 } else {
537 renderer->row[offset] = current | FLAG_FINALIZED;
538 }
539 }
540}
541
542#define BACKGROUND_DRAW_PIXEL_16_NORMAL \
543 pixelData = tileData & 0xF; \
544 if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
545 _composite(renderer, outX, renderer->normalPalette[pixelData | paletteData] | flags); \
546 } \
547 tileData >>= 4;
548
549#define BACKGROUND_DRAW_PIXEL_16_VARIANT \
550 pixelData = tileData & 0xF; \
551 if (tileData & 0xF && !(renderer->row[outX] & FLAG_FINALIZED)) { \
552 _composite(renderer, outX, renderer->variantPalette[pixelData | paletteData] | flags); \
553 } \
554 tileData >>= 4;
555
556#define BACKGROUND_DRAW_PIXEL_256_NORMAL \
557 pixelData = tileData & 0xFF; \
558 if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
559 _composite(renderer, outX, renderer->normalPalette[pixelData] | flags); \
560 } \
561 tileData >>= 8;
562
563#define BACKGROUND_DRAW_PIXEL_256_VARIANT \
564 pixelData = tileData & 0xFF; \
565 if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
566 _composite(renderer, outX, renderer->variantPalette[pixelData] | flags); \
567 } \
568 tileData >>= 8;
569
570#define BACKGROUND_TEXT_SELECT_CHARACTER \
571 localX = tileX * 8 + inX; \
572 xBase = localX & 0xF8; \
573 if (background->size & 1) { \
574 xBase += (localX & 0x100) << 5; \
575 } \
576 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
577 mapData.packed = renderer->d.vram[screenBase]; \
578 if (!mapData.vflip) { \
579 localY = inY & 0x7; \
580 } else { \
581 localY = 7 - (inY & 0x7); \
582 }
583
584#define BACKGROUND_MODE_0_TILE_16_LOOP(TYPE) \
585 uint32_t tileData; \
586 int paletteData, pixelData; \
587 for (; tileX < 30; ++tileX) { \
588 BACKGROUND_TEXT_SELECT_CHARACTER; \
589 paletteData = mapData.palette << 4; \
590 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY; \
591 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
592 if (tileData) { \
593 if (!mapData.hflip) { \
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; \
610 } else { \
611 outX += 7; \
612 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
613 --outX; \
614 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
615 --outX; \
616 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
617 --outX; \
618 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
619 --outX; \
620 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
621 --outX; \
622 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
623 --outX; \
624 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
625 --outX; \
626 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
627 outX += 8; \
628 } \
629 } else { \
630 outX += 8; \
631 } \
632 }
633
634#define BACKGROUND_MODE_0_TILE_256_LOOP(TYPE) \
635 uint32_t tileData; \
636 int pixelData; \
637 for (; tileX < 30; ++tileX) { \
638 BACKGROUND_TEXT_SELECT_CHARACTER; \
639 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1); \
640 if (!mapData.hflip) { \
641 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
642 if (tileData) { \
643 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
644 ++outX; \
645 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
646 ++outX; \
647 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
648 ++outX; \
649 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
650 ++outX; \
651 } else { \
652 outX += 4; \
653 } \
654 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
655 if (tileData) { \
656 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
657 ++outX; \
658 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
659 ++outX; \
660 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
661 ++outX; \
662 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
663 ++outX; \
664 } else { \
665 outX += 4; \
666 } \
667 } else { \
668 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
669 if (tileData) { \
670 outX += 3; \
671 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
672 --outX; \
673 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
674 --outX; \
675 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
676 --outX; \
677 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
678 outX += 4; \
679 } else { \
680 outX += 4; \
681 } \
682 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
683 if (tileData) { \
684 outX += 3; \
685 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
686 --outX; \
687 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
688 --outX; \
689 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
690 --outX; \
691 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
692 outX += 4; \
693 } else { \
694 outX += 4; \
695 } \
696 } \
697 }
698
699static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
700 int inX = background->x;
701 int inY = y + background->y;
702 union GBATextMapData mapData;
703
704 unsigned yBase = inY & 0xF8;
705 if (background->size == 2) {
706 yBase += inY & 0x100;
707 } else if (background->size == 3) {
708 yBase += (inY & 0x100) << 1;
709 }
710
711 int localX;
712 int localY;
713
714 unsigned xBase;
715
716 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
717 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
718 flags |= FLAG_TARGET_2 * background->target2;
719
720 uint32_t screenBase;
721 uint32_t charBase;
722 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
723
724 int outX = 0;
725 int tileX = 0;
726 if (inX & 0x7) {
727 uint32_t tileData;
728 int pixelData, paletteData;
729 BACKGROUND_TEXT_SELECT_CHARACTER;
730
731 int end = 0x8 - (inX & 0x7);
732 if (!background->multipalette) {
733 paletteData = mapData.palette << 4;
734 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
735 tileData = ((uint32_t*)renderer->d.vram)[charBase];
736 if (!mapData.hflip) {
737 tileData >>= 4 * (inX & 0x7);
738 if (!variant) {
739 for (outX = 0; outX < end; ++outX) {
740 BACKGROUND_DRAW_PIXEL_16_NORMAL;
741 }
742 } else {
743 for (outX = 0; outX < end; ++outX) {
744 BACKGROUND_DRAW_PIXEL_16_VARIANT;
745 }
746 }
747 } else {
748 if (!variant) {
749 for (outX = end; outX--;) {
750 BACKGROUND_DRAW_PIXEL_16_NORMAL;
751 }
752 } else {
753 for (outX = end; outX--;) {
754 BACKGROUND_DRAW_PIXEL_16_VARIANT;
755 }
756 }
757 }
758 } else {
759 // TODO: hflip
760 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
761 outX = 0;
762 int end2 = end - 4;
763 int shift = inX & 0x3;
764 if (end2 > 0) {
765 tileData = ((uint32_t*)renderer->d.vram)[charBase];
766 tileData >>= 8 * shift;
767 shift = 0;
768 if (!variant) {
769 for (; outX < end2; ++outX) {
770 BACKGROUND_DRAW_PIXEL_256_NORMAL;
771 }
772 } else {
773 for (; outX < end2; ++outX) {
774 BACKGROUND_DRAW_PIXEL_256_VARIANT;
775 }
776 }
777 }
778
779 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
780 tileData >>= 8 * shift;
781 if (!variant) {
782 for (; outX < end; ++outX) {
783 BACKGROUND_DRAW_PIXEL_256_NORMAL;
784 }
785 } else {
786 for (; outX < end; ++outX) {
787 BACKGROUND_DRAW_PIXEL_256_VARIANT;
788 }
789 }
790 }
791
792 tileX = 30;
793 BACKGROUND_TEXT_SELECT_CHARACTER;
794 if (!background->multipalette) {
795 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
796 tileData = ((uint32_t*)renderer->d.vram)[charBase];
797 paletteData = mapData.palette << 4;
798 if (!mapData.hflip) {
799 if (!variant) {
800 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
801 BACKGROUND_DRAW_PIXEL_16_NORMAL;
802 }
803 } else {
804 for (outX = VIDEO_HORIZONTAL_PIXELS - 8 + end; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
805 BACKGROUND_DRAW_PIXEL_16_VARIANT;
806 }
807 }
808 } else {
809 tileData >>= 4 * end;
810 if (!variant) {
811 for (outX = VIDEO_HORIZONTAL_PIXELS - 1; outX > VIDEO_HORIZONTAL_PIXELS - 8; --outX) {
812 BACKGROUND_DRAW_PIXEL_16_NORMAL;
813 }
814 } else {
815 for (outX = VIDEO_HORIZONTAL_PIXELS - 1; outX > VIDEO_HORIZONTAL_PIXELS - 8; --outX) {
816 BACKGROUND_DRAW_PIXEL_16_VARIANT;
817 }
818 }
819 }
820 } else {
821 // TODO: hflip
822 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
823 outX = VIDEO_HORIZONTAL_PIXELS - 8 + end;
824 int end2 = 4 - end;
825 if (end2 > 0) {
826 tileData = ((uint32_t*)renderer->d.vram)[charBase];
827 if (!variant) {
828 for (; outX < VIDEO_HORIZONTAL_PIXELS - end2; ++outX) {
829 BACKGROUND_DRAW_PIXEL_256_NORMAL;
830 }
831 } else {
832 for (; outX < VIDEO_HORIZONTAL_PIXELS - end2; ++outX) {
833 BACKGROUND_DRAW_PIXEL_256_VARIANT;
834 }
835 }
836 ++charBase;
837 }
838
839 tileData = ((uint32_t*)renderer->d.vram)[charBase];
840 if (!variant) {
841 for (; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
842 BACKGROUND_DRAW_PIXEL_256_NORMAL;
843 }
844 } else {
845 for (; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
846 BACKGROUND_DRAW_PIXEL_256_VARIANT;
847 }
848 }
849 }
850
851 tileX = 1;
852 outX = end;
853 }
854
855 if (!background->multipalette) {
856 if (!variant) {
857 BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
858 } else {
859 BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
860 }
861 } else {
862 if (!variant) {
863 BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
864 } else {
865 BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
866 }
867 }
868}
869
870#define BACKGROUND_BITMAP_INIT \
871 (void)(unused); \
872 int32_t x = background->sx - background->dx; \
873 int32_t y = background->sy - background->dy; \
874 int32_t localX; \
875 int32_t localY; \
876 \
877 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
878 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
879 flags |= FLAG_TARGET_2 * background->target2; \
880 int variant = background->target1 && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
881
882#define BACKGROUND_BITMAP_ITERATE(W, H) \
883 x += background->dx; \
884 y += background->dy; \
885 \
886 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
887 continue; \
888 } else { \
889 localX = x; \
890 localY = y; \
891 }
892
893static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
894 int sizeAdjusted = 0x8000 << background->size;
895
896 BACKGROUND_BITMAP_INIT;
897
898 uint32_t screenBase = background->screenBase;
899 uint32_t charBase = background->charBase;
900 uint8_t mapData;
901 uint8_t tileData;
902
903 int outX;
904 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
905 x += background->dx;
906 y += background->dy;
907
908 if (background->overflow) {
909 localX = x & (sizeAdjusted - 1);
910 localY = y & (sizeAdjusted - 1);
911 } else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
912 continue;
913 } else {
914 localX = x;
915 localY = y;
916 }
917 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
918 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
919
920 if (tileData && !(renderer->row[outX] & FLAG_FINALIZED)) {
921 if (!variant) {
922 _composite(renderer, outX, renderer->normalPalette[tileData] | flags);
923 } else {
924 _composite(renderer, outX, renderer->variantPalette[tileData] | flags);
925 }
926 }
927 }
928}
929
930static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
931 BACKGROUND_BITMAP_INIT;
932
933 uint16_t color;
934 uint32_t color32;
935
936 int outX;
937 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
938 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
939
940 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
941 color32 = 0;
942 color32 |= (color << 3) & 0xF8;
943 color32 |= (color << 6) & 0xF800;
944 color32 |= (color << 9) & 0xF80000;
945
946 if (!(renderer->row[outX] & FLAG_FINALIZED)) {
947 if (!variant) {
948 _composite(renderer, outX, color32 | flags);
949 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
950 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
951 } else if (renderer->blendEffect == BLEND_DARKEN) {
952 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
953 }
954 }
955 }
956}
957
958static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
959 BACKGROUND_BITMAP_INIT;
960
961 uint16_t color;
962 uint32_t offset = 0;
963 if (renderer->dispcnt.frameSelect) {
964 offset = 0xA000;
965 }
966
967 int outX;
968 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
969 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
970
971 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
972
973 if (color && !(renderer->row[outX] & FLAG_FINALIZED)) {
974 if (!variant) {
975 _composite(renderer, outX, renderer->normalPalette[color] | flags);
976 } else {
977 _composite(renderer, outX, renderer->variantPalette[color] | flags);
978 }
979 }
980 }
981}
982
983static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
984 BACKGROUND_BITMAP_INIT;
985
986 uint16_t color;
987 uint32_t color32;
988 uint32_t offset = 0;
989 if (renderer->dispcnt.frameSelect) {
990 offset = 0xA000;
991 }
992
993 int outX;
994 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
995 BACKGROUND_BITMAP_ITERATE(160, 128);
996
997 color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
998 color32 = 0;
999 color32 |= (color << 3) & 0xF8;
1000 color32 |= (color << 6) & 0xF800;
1001 color32 |= (color << 9) & 0xF80000;
1002
1003 if (!(renderer->row[outX] & FLAG_FINALIZED)) {
1004 if (!variant) {
1005 _composite(renderer, outX, color32 | flags);
1006 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1007 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
1008 } else if (renderer->blendEffect == BLEND_DARKEN) {
1009 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
1010 }
1011 }
1012 }
1013}
1014
1015static const int _objSizes[32] = {
1016 8, 8,
1017 16, 16,
1018 32, 32,
1019 64, 64,
1020 16, 8,
1021 32, 8,
1022 32, 16,
1023 64, 32,
1024 8, 16,
1025 8, 32,
1026 16, 32,
1027 32, 64,
1028 0, 0,
1029 0, 0,
1030 0, 0,
1031 0, 0
1032};
1033
1034#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1035 SPRITE_YBASE_ ## DEPTH(inY); \
1036 int outX = x >= start ? x : start; \
1037 int condition = x + width; \
1038 if (end < condition) { \
1039 condition = end; \
1040 } \
1041 for (; outX < condition; ++outX) { \
1042 int inX = outX - x; \
1043 if (sprite->hflip) { \
1044 inX = width - inX - 1; \
1045 } \
1046 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1047 continue; \
1048 } \
1049 SPRITE_XBASE_ ## DEPTH(inX); \
1050 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1051 }
1052
1053#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1054 for (int outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1055 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1056 continue; \
1057 } \
1058 int inX = outX - x; \
1059 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1060 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1061 \
1062 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1063 continue; \
1064 } \
1065 \
1066 SPRITE_YBASE_ ## DEPTH(localY); \
1067 SPRITE_XBASE_ ## DEPTH(localX); \
1068 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1069 }
1070
1071#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1072#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1073
1074#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1075 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1076 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1077 if (tileData && !(renderer->spriteLayer[outX])) { \
1078 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1079 }
1080
1081#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1082 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1083 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1084 if (tileData && !(renderer->spriteLayer[outX])) { \
1085 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1086 }
1087
1088#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1089#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1090
1091#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1092 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1093 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1094 if (tileData && !(renderer->spriteLayer[outX])) { \
1095 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1096 }
1097
1098#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1099 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1100 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1101 if (tileData && !(renderer->spriteLayer[outX])) { \
1102 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1103 }
1104
1105static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1106 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1107 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1108 int start = renderer->start;
1109 int end = renderer->end;
1110 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1111 return;
1112 }
1113 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1114 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1115 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1116 int x = sprite->x;
1117 int inY = y - sprite->y;
1118 if (sprite->y + height - 256 >= 0) {
1119 inY += 256;
1120 }
1121 if (sprite->vflip) {
1122 inY = height - inY - 1;
1123 }
1124 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1125 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1126 if (!sprite->multipalette) {
1127 if (!variant) {
1128 SPRITE_NORMAL_LOOP(16, NORMAL);
1129 } else {
1130 SPRITE_NORMAL_LOOP(16, VARIANT);
1131 }
1132 } else {
1133 if (!variant) {
1134 SPRITE_NORMAL_LOOP(256, NORMAL);
1135 } else {
1136 SPRITE_NORMAL_LOOP(256, VARIANT);
1137 }
1138 }
1139}
1140
1141static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1142 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1143 int totalWidth = width << sprite->doublesize;
1144 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1145 int totalHeight = height << sprite->doublesize;
1146 int start = renderer->start;
1147 int end = renderer->end;
1148 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1149 return;
1150 }
1151 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1152 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1153 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1154 int x = sprite->x;
1155 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1156 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1157 int variant = renderer->target1Obj && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1158 int inY = y - sprite->y;
1159 if (inY < 0) {
1160 inY += 256;
1161 }
1162 if (!sprite->multipalette) {
1163 if (!variant) {
1164 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1165 } else {
1166 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1167 }
1168 } else {
1169 if (!variant) {
1170 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1171 } else {
1172 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1173 }
1174 }
1175}
1176
1177static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority) {
1178 int x;
1179 for (x = 0; x < 240; ++x) {
1180 uint32_t color = renderer->spriteLayer[x];
1181 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(renderer->row[x] & FLAG_FINALIZED)) {
1182 _composite(renderer, x, color & ~FLAG_FINALIZED);
1183 }
1184 }
1185}
1186
1187static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1188 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1189 for (int i = 0; i < 512; ++i) {
1190 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1191 }
1192 } else if (renderer->blendEffect == BLEND_DARKEN) {
1193 for (int i = 0; i < 512; ++i) {
1194 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1195 }
1196 } else {
1197 for (int i = 0; i < 512; ++i) {
1198 renderer->variantPalette[i] = renderer->normalPalette[i];
1199 }
1200 }
1201}
1202
1203static inline uint32_t _brighten(uint32_t color, int y) {
1204 uint32_t c = 0;
1205 uint32_t a;
1206 a = color & 0xF8;
1207 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1208
1209 a = color & 0xF800;
1210 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1211
1212 a = color & 0xF80000;
1213 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1214 return c;
1215}
1216
1217static inline uint32_t _darken(uint32_t color, int y) {
1218 uint32_t c = 0;
1219 uint32_t a;
1220 a = color & 0xF8;
1221 c |= (a - (a * y) / 16) & 0xF8;
1222
1223 a = color & 0xF800;
1224 c |= (a - (a * y) / 16) & 0xF800;
1225
1226 a = color & 0xF80000;
1227 c |= (a - (a * y) / 16) & 0xF80000;
1228 return c;
1229}
1230
1231static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
1232 uint32_t c = 0;
1233 uint32_t a, b;
1234 a = colorA & 0xF8;
1235 b = colorB & 0xF8;
1236 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1237 if (c & 0x00000100) {
1238 c = 0x000000F8;
1239 }
1240
1241 a = colorA & 0xF800;
1242 b = colorB & 0xF800;
1243 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1244 if (c & 0x00010000) {
1245 c = (c & 0x000000F8) | 0x0000F800;
1246 }
1247
1248 a = colorA & 0xF80000;
1249 b = colorB & 0xF80000;
1250 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1251 if (c & 0x01000000) {
1252 c = (c & 0x0000F8F8) | 0x00F80000;
1253 }
1254 return c;
1255}