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