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, unsigned priority);
37
38static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);
39static inline color_t _brighten(color_t color, int y);
40static inline color_t _darken(color_t color, int y);
41static color_t _mix(int weightA, color_t colorA, int weightB, color_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 softwareRenderer->win0.priority = 0;
85 softwareRenderer->win1.priority = 1;
86 softwareRenderer->objwin.priority = 2;
87 softwareRenderer->winout.priority = 3;
88
89 for (i = 0; i < 4; ++i) {
90 struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
91 bg->index = i;
92 bg->enabled = 0;
93 bg->priority = 0;
94 bg->charBase = 0;
95 bg->mosaic = 0;
96 bg->multipalette = 0;
97 bg->screenBase = 0;
98 bg->overflow = 0;
99 bg->size = 0;
100 bg->target1 = 0;
101 bg->target2 = 0;
102 bg->x = 0;
103 bg->y = 0;
104 bg->refx = 0;
105 bg->refy = 0;
106 bg->dx = 256;
107 bg->dmx = 0;
108 bg->dy = 0;
109 bg->dmy = 256;
110 bg->sx = 0;
111 bg->sy = 0;
112 }
113
114 pthread_mutex_init(&softwareRenderer->mutex, 0);
115 pthread_cond_init(&softwareRenderer->upCond, 0);
116 pthread_cond_init(&softwareRenderer->downCond, 0);
117}
118
119static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) {
120 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
121
122 pthread_mutex_lock(&softwareRenderer->mutex);
123 pthread_cond_broadcast(&softwareRenderer->upCond);
124 pthread_mutex_unlock(&softwareRenderer->mutex);
125
126 pthread_mutex_destroy(&softwareRenderer->mutex);
127 pthread_cond_destroy(&softwareRenderer->upCond);
128 pthread_cond_destroy(&softwareRenderer->downCond);
129}
130
131static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
132 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
133 switch (address) {
134 case REG_DISPCNT:
135 softwareRenderer->dispcnt.packed = value;
136 GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer);
137 break;
138 case REG_BG0CNT:
139 value &= 0xFFCF;
140 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[0], value);
141 break;
142 case REG_BG1CNT:
143 value &= 0xFFCF;
144 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[1], value);
145 break;
146 case REG_BG2CNT:
147 value &= 0xFFCF;
148 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[2], value);
149 break;
150 case REG_BG3CNT:
151 value &= 0xFFCF;
152 GBAVideoSoftwareRendererWriteBGCNT(softwareRenderer, &softwareRenderer->bg[3], value);
153 break;
154 case REG_BG0HOFS:
155 value &= 0x01FF;
156 softwareRenderer->bg[0].x = value;
157 break;
158 case REG_BG0VOFS:
159 value &= 0x01FF;
160 softwareRenderer->bg[0].y = value;
161 break;
162 case REG_BG1HOFS:
163 value &= 0x01FF;
164 softwareRenderer->bg[1].x = value;
165 break;
166 case REG_BG1VOFS:
167 value &= 0x01FF;
168 softwareRenderer->bg[1].y = value;
169 break;
170 case REG_BG2HOFS:
171 value &= 0x01FF;
172 softwareRenderer->bg[2].x = value;
173 break;
174 case REG_BG2VOFS:
175 value &= 0x01FF;
176 softwareRenderer->bg[2].y = value;
177 break;
178 case REG_BG3HOFS:
179 value &= 0x01FF;
180 softwareRenderer->bg[3].x = value;
181 break;
182 case REG_BG3VOFS:
183 value &= 0x01FF;
184 softwareRenderer->bg[3].y = value;
185 break;
186 case REG_BG2PA:
187 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[2], value);
188 break;
189 case REG_BG2PB:
190 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[2], value);
191 break;
192 case REG_BG2PC:
193 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[2], value);
194 break;
195 case REG_BG2PD:
196 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[2], value);
197 break;
198 case REG_BG2X_LO:
199 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[2], value);
200 break;
201 case REG_BG2X_HI:
202 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[2], value);
203 break;
204 case REG_BG2Y_LO:
205 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[2], value);
206 break;
207 case REG_BG2Y_HI:
208 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[2], value);
209 break;
210 case REG_BG3PA:
211 GBAVideoSoftwareRendererWriteBGPA(&softwareRenderer->bg[3], value);
212 break;
213 case REG_BG3PB:
214 GBAVideoSoftwareRendererWriteBGPB(&softwareRenderer->bg[3], value);
215 break;
216 case REG_BG3PC:
217 GBAVideoSoftwareRendererWriteBGPC(&softwareRenderer->bg[3], value);
218 break;
219 case REG_BG3PD:
220 GBAVideoSoftwareRendererWriteBGPD(&softwareRenderer->bg[3], value);
221 break;
222 case REG_BG3X_LO:
223 GBAVideoSoftwareRendererWriteBGX_LO(&softwareRenderer->bg[3], value);
224 break;
225 case REG_BG3X_HI:
226 GBAVideoSoftwareRendererWriteBGX_HI(&softwareRenderer->bg[3], value);
227 break;
228 case REG_BG3Y_LO:
229 GBAVideoSoftwareRendererWriteBGY_LO(&softwareRenderer->bg[3], value);
230 break;
231 case REG_BG3Y_HI:
232 GBAVideoSoftwareRendererWriteBGY_HI(&softwareRenderer->bg[3], value);
233 break;
234 case REG_BLDCNT:
235 GBAVideoSoftwareRendererWriteBLDCNT(softwareRenderer, value);
236 break;
237 case REG_BLDALPHA:
238 softwareRenderer->blda = value & 0x1F;
239 if (softwareRenderer->blda > 0x10) {
240 softwareRenderer->blda = 0x10;
241 }
242 softwareRenderer->bldb = (value >> 8) & 0x1F;
243 if (softwareRenderer->bldb > 0x10) {
244 softwareRenderer->bldb = 0x10;
245 }
246 break;
247 case REG_BLDY:
248 softwareRenderer->bldy = value & 0x1F;
249 if (softwareRenderer->bldy > 0x10) {
250 softwareRenderer->bldy = 0x10;
251 }
252 _updatePalettes(softwareRenderer);
253 break;
254 case REG_WIN0H:
255 softwareRenderer->win0H.packed = value;
256 if (softwareRenderer->win0H.start > softwareRenderer->win0H.end || softwareRenderer->win0H.end > VIDEO_HORIZONTAL_PIXELS) {
257 softwareRenderer->win0H.end = VIDEO_HORIZONTAL_PIXELS;
258 }
259 break;
260 case REG_WIN1H:
261 softwareRenderer->win1H.packed = value;
262 if (softwareRenderer->win1H.start > softwareRenderer->win1H.end || softwareRenderer->win1H.end > VIDEO_HORIZONTAL_PIXELS) {
263 softwareRenderer->win1H.end = VIDEO_HORIZONTAL_PIXELS;
264 }
265 break;
266 case REG_WIN0V:
267 softwareRenderer->win0V.packed = value;
268 if (softwareRenderer->win0V.start > softwareRenderer->win0V.end || softwareRenderer->win0V.end > VIDEO_HORIZONTAL_PIXELS) {
269 softwareRenderer->win0V.end = VIDEO_VERTICAL_PIXELS;
270 }
271 break;
272 case REG_WIN1V:
273 softwareRenderer->win1V.packed = value;
274 if (softwareRenderer->win1V.start > softwareRenderer->win1V.end || softwareRenderer->win1V.end > VIDEO_HORIZONTAL_PIXELS) {
275 softwareRenderer->win1V.end = VIDEO_VERTICAL_PIXELS;
276 }
277 break;
278 case REG_WININ:
279 softwareRenderer->win0.packed = value;
280 softwareRenderer->win1.packed = value >> 8;
281 break;
282 case REG_WINOUT:
283 softwareRenderer->winout.packed = value;
284 softwareRenderer->objwin.packed = value >> 8;
285 break;
286 default:
287 GBALog(0, GBA_LOG_STUB, "Stub video register write: %03x", address);
288 }
289 return value;
290}
291
292static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
293 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
294 if ((oam & 0x3) != 0x3) {
295 oam >>= 2;
296 struct GBAObj* sprite = &renderer->oam->obj[oam];
297 int enabled = sprite->transformed || !sprite->disable;
298 enabled <<= (oam & 0x1F);
299 softwareRenderer->enabledBitmap[oam >> 5] = (softwareRenderer->enabledBitmap[oam >> 5] & ~(1 << (oam & 0x1F))) | enabled;
300 }
301}
302
303static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
304 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
305#ifdef COLOR_16_BIT
306 color_t color = value;
307#else
308 color_t color = 0;
309 color |= (value << 3) & 0xF8;
310 color |= (value << 6) & 0xF800;
311 color |= (value << 9) & 0xF80000;
312#endif
313 softwareRenderer->normalPalette[address >> 1] = color;
314 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
315 softwareRenderer->variantPalette[address >> 1] = _brighten(color, softwareRenderer->bldy);
316 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
317 softwareRenderer->variantPalette[address >> 1] = _darken(color, softwareRenderer->bldy);
318 }
319}
320
321#define BREAK_WINDOW(WIN) \
322 int activeWindow; \
323 int startX = 0; \
324 if (softwareRenderer->WIN ## H.end > 0) { \
325 for (activeWindow = 0; activeWindow < softwareRenderer->nWindows; ++activeWindow) { \
326 if (softwareRenderer->WIN ## H.start < softwareRenderer->windows[activeWindow].endX) { \
327 struct Window oldWindow = softwareRenderer->windows[activeWindow]; \
328 if (softwareRenderer->WIN ## H.start > startX) { \
329 int nextWindow = softwareRenderer->nWindows; \
330 ++softwareRenderer->nWindows; \
331 for (; nextWindow > activeWindow; --nextWindow) { \
332 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1]; \
333 } \
334 softwareRenderer->windows[activeWindow].endX = softwareRenderer->WIN ## H.start; \
335 ++activeWindow; \
336 } \
337 softwareRenderer->windows[activeWindow].control = softwareRenderer->WIN; \
338 softwareRenderer->windows[activeWindow].endX = softwareRenderer->WIN ## H.end; \
339 if (softwareRenderer->WIN ## H.end >= oldWindow.endX) { \
340 for (++activeWindow; softwareRenderer->WIN ## H.end >= softwareRenderer->windows[activeWindow].endX && softwareRenderer->nWindows > 1; ++activeWindow) { \
341 softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1]; \
342 --softwareRenderer->nWindows; \
343 } \
344 } else { \
345 ++activeWindow; \
346 int nextWindow = softwareRenderer->nWindows; \
347 ++softwareRenderer->nWindows; \
348 for (; nextWindow > activeWindow; --nextWindow) { \
349 softwareRenderer->windows[nextWindow] = softwareRenderer->windows[nextWindow - 1]; \
350 } \
351 softwareRenderer->windows[activeWindow] = oldWindow; \
352 } \
353 break; \
354 } \
355 startX = softwareRenderer->windows[activeWindow].endX; \
356 } \
357 }
358
359static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
360 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
361 if (renderer->frameskip > 0) {
362 return;
363 }
364 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
365 if (softwareRenderer->dispcnt.forcedBlank) {
366 int x;
367 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
368 row[x] = GBA_COLOR_WHITE;
369 }
370 return;
371 }
372
373 memset(softwareRenderer->spriteLayer, 0, sizeof(softwareRenderer->spriteLayer));
374
375 softwareRenderer->windows[0].endX = VIDEO_HORIZONTAL_PIXELS;
376 softwareRenderer->nWindows = 1;
377 if (softwareRenderer->dispcnt.win0Enable || softwareRenderer->dispcnt.win1Enable || softwareRenderer->dispcnt.objwinEnable) {
378 softwareRenderer->windows[0].control = softwareRenderer->winout;
379 if (softwareRenderer->dispcnt.win1Enable && y < softwareRenderer->win1V.end && y >= softwareRenderer->win1V.start) {
380 BREAK_WINDOW(win1);
381 }
382 if (softwareRenderer->dispcnt.win0Enable && y < softwareRenderer->win0V.end && y >= softwareRenderer->win0V.start) {
383 BREAK_WINDOW(win0);
384 }
385 } else {
386 softwareRenderer->windows[0].control.packed = 0xFF;
387 }
388
389 int w;
390 int x = 0;
391 for (w = 0; w < softwareRenderer->nWindows; ++w) {
392 // TOOD: handle objwin on backdrop
393 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
394 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
395 backdrop |= softwareRenderer->normalPalette[0];
396 } else {
397 backdrop |= softwareRenderer->variantPalette[0];
398 }
399 for (; x < softwareRenderer->windows[w].endX; ++x) {
400 softwareRenderer->row[x] = backdrop;
401 }
402 }
403
404 _drawScanline(softwareRenderer, y);
405
406 if (softwareRenderer->target2Bd) {
407 x = 0;
408 for (w = 0; w < softwareRenderer->nWindows; ++w) {
409 uint32_t backdrop = FLAG_UNWRITTEN | FLAG_PRIORITY | FLAG_IS_BACKGROUND;
410 if (!softwareRenderer->target1Bd || softwareRenderer->blendEffect == BLEND_NONE || softwareRenderer->blendEffect == BLEND_ALPHA || !softwareRenderer->windows[w].control.blendEnable) {
411 backdrop |= softwareRenderer->normalPalette[0];
412 } else {
413 backdrop |= softwareRenderer->variantPalette[0];
414 }
415 for (; x < softwareRenderer->windows[w].endX; ++x) {
416 uint32_t color = softwareRenderer->row[x];
417 if (color & FLAG_TARGET_1 && !(color & FLAG_FINALIZED)) {
418 softwareRenderer->row[x] = _mix(softwareRenderer->bldb, backdrop, softwareRenderer->blda, color);
419 }
420 }
421 }
422 }
423
424#ifdef COLOR_16_BIT
425 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
426 row[x] = softwareRenderer->row[x];
427 }
428#else
429 memcpy(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
430#endif
431}
432
433static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer) {
434 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
435
436 pthread_mutex_lock(&softwareRenderer->mutex);
437 if (renderer->frameskip > 0) {
438 --renderer->frameskip;
439 } else {
440 renderer->framesPending++;
441 pthread_cond_broadcast(&softwareRenderer->upCond);
442 if (!renderer->turbo) {
443 pthread_cond_wait(&softwareRenderer->downCond, &softwareRenderer->mutex);
444 }
445 }
446 pthread_mutex_unlock(&softwareRenderer->mutex);
447
448 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx;
449 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy;
450 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx;
451 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
452}
453
454static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) {
455 renderer->bg[0].enabled = renderer->dispcnt.bg0Enable;
456 renderer->bg[1].enabled = renderer->dispcnt.bg1Enable;
457 renderer->bg[2].enabled = renderer->dispcnt.bg2Enable;
458 renderer->bg[3].enabled = renderer->dispcnt.bg3Enable;
459}
460
461static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) {
462 (void)(renderer);
463 union GBARegisterBGCNT reg = { .packed = value };
464 bg->priority = reg.priority;
465 bg->charBase = reg.charBase << 14;
466 bg->mosaic = reg.mosaic;
467 bg->multipalette = reg.multipalette;
468 bg->screenBase = reg.screenBase << 11;
469 bg->overflow = reg.overflow;
470 bg->size = reg.size;
471}
472
473static void GBAVideoSoftwareRendererWriteBGPA(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
474 bg->dx = value;
475}
476
477static void GBAVideoSoftwareRendererWriteBGPB(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
478 bg->dmx = value;
479}
480
481static void GBAVideoSoftwareRendererWriteBGPC(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
482 bg->dy = value;
483}
484
485static void GBAVideoSoftwareRendererWriteBGPD(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
486 bg->dmy = value;
487}
488
489static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
490 bg->refx = (bg->refx & 0xFFFF0000) | value;
491 bg->sx = bg->refx;
492}
493
494static void GBAVideoSoftwareRendererWriteBGX_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
495 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
496 bg->refx <<= 4;
497 bg->refx >>= 4;
498 bg->sx = bg->refx;
499}
500
501static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
502 bg->refy = (bg->refy & 0xFFFF0000) | value;
503 bg->sy = bg->refy;
504}
505
506static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value) {
507 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
508 bg->refy <<= 4;
509 bg->refy >>= 4;
510 bg->sy = bg->refy;
511}
512
513static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value) {
514 union {
515 struct {
516 unsigned target1Bg0 : 1;
517 unsigned target1Bg1 : 1;
518 unsigned target1Bg2 : 1;
519 unsigned target1Bg3 : 1;
520 unsigned target1Obj : 1;
521 unsigned target1Bd : 1;
522 enum BlendEffect effect : 2;
523 unsigned target2Bg0 : 1;
524 unsigned target2Bg1 : 1;
525 unsigned target2Bg2 : 1;
526 unsigned target2Bg3 : 1;
527 unsigned target2Obj : 1;
528 unsigned target2Bd : 1;
529 };
530 uint16_t packed;
531 } bldcnt = { .packed = value };
532
533 enum BlendEffect oldEffect = renderer->blendEffect;
534
535 renderer->bg[0].target1 = bldcnt.target1Bg0;
536 renderer->bg[1].target1 = bldcnt.target1Bg1;
537 renderer->bg[2].target1 = bldcnt.target1Bg2;
538 renderer->bg[3].target1 = bldcnt.target1Bg3;
539 renderer->bg[0].target2 = bldcnt.target2Bg0;
540 renderer->bg[1].target2 = bldcnt.target2Bg1;
541 renderer->bg[2].target2 = bldcnt.target2Bg2;
542 renderer->bg[3].target2 = bldcnt.target2Bg3;
543
544 renderer->blendEffect = bldcnt.effect;
545 renderer->target1Obj = bldcnt.target1Obj;
546 renderer->target1Bd = bldcnt.target1Bd;
547 renderer->target2Obj = bldcnt.target2Obj;
548 renderer->target2Bd = bldcnt.target2Bd;
549
550 if (oldEffect != renderer->blendEffect) {
551 _updatePalettes(renderer);
552 }
553}
554
555#define TEST_LAYER_ENABLED(X) \
556 (renderer->bg[X].enabled && \
557 (renderer->currentWindow.bg ## X ## Enable || \
558 (renderer->dispcnt.objwinEnable && renderer->objwin.bg ## X ## Enable)) && \
559 renderer->bg[X].priority == priority)
560
561static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
562 int w;
563 renderer->end = 0;
564 if (renderer->dispcnt.objEnable) {
565 for (w = 0; w < renderer->nWindows; ++w) {
566 renderer->start = renderer->end;
567 renderer->end = renderer->windows[w].endX;
568 renderer->currentWindow = renderer->windows[w].control;
569 if (!renderer->currentWindow.objEnable) {
570 continue;
571 }
572 int i, j;
573 for (j = 0; j < 4; ++j) {
574 uint32_t bitmap = renderer->enabledBitmap[j];
575 if (!bitmap) {
576 continue;
577 }
578 for (i = j * 32; i < (j + 1) * 32; ++i) {
579 if (bitmap & 1) {
580 struct GBAObj* sprite = &renderer->d.oam->obj[i];
581 if (sprite->transformed) {
582 _preprocessTransformedSprite(renderer, &renderer->d.oam->tobj[i], y);
583 } else {
584 _preprocessSprite(renderer, sprite, y);
585 }
586 }
587 bitmap >>= 1;
588 }
589 }
590 }
591 }
592
593 int priority;
594 for (priority = 0; priority < 4; ++priority) {
595 _postprocessSprite(renderer, priority);
596 renderer->end = 0;
597 for (w = 0; w < renderer->nWindows; ++w) {
598 renderer->start = renderer->end;
599 renderer->end = renderer->windows[w].endX;
600 renderer->currentWindow = renderer->windows[w].control;
601 if (TEST_LAYER_ENABLED(0) && renderer->dispcnt.mode < 2) {
602 _drawBackgroundMode0(renderer, &renderer->bg[0], y);
603 }
604 if (TEST_LAYER_ENABLED(1) && renderer->dispcnt.mode < 2) {
605 _drawBackgroundMode0(renderer, &renderer->bg[1], y);
606 }
607 if (TEST_LAYER_ENABLED(2)) {
608 switch (renderer->dispcnt.mode) {
609 case 0:
610 _drawBackgroundMode0(renderer, &renderer->bg[2], y);
611 break;
612 case 1:
613 case 2:
614 _drawBackgroundMode2(renderer, &renderer->bg[2], y);
615 break;
616 case 3:
617 _drawBackgroundMode3(renderer, &renderer->bg[2], y);
618 break;
619 case 4:
620 _drawBackgroundMode4(renderer, &renderer->bg[2], y);
621 break;
622 case 5:
623 _drawBackgroundMode5(renderer, &renderer->bg[2], y);
624 break;
625 }
626 renderer->bg[2].sx += renderer->bg[2].dmx;
627 renderer->bg[2].sy += renderer->bg[2].dmy;
628 }
629 if (TEST_LAYER_ENABLED(3)) {
630 switch (renderer->dispcnt.mode) {
631 case 0:
632 _drawBackgroundMode0(renderer, &renderer->bg[3], y);
633 break;
634 case 2:
635 _drawBackgroundMode2(renderer, &renderer->bg[3], y);
636 break;
637 }
638 renderer->bg[3].sx += renderer->bg[3].dmx;
639 renderer->bg[3].sy += renderer->bg[3].dmy;
640 }
641 }
642 }
643}
644
645static void _composite(struct GBAVideoSoftwareRenderer* renderer, int offset, uint32_t color, uint32_t current) {
646 // We stash the priority on the top bits so we can do a one-operator comparison
647 // The lower the number, the higher the priority, and sprites take precendence over backgrounds
648 // We want to do special processing if the color pixel is target 1, however
649 if ((color & 0xF8000000) < (current & 0xF8000000)) {
650 if (current & FLAG_UNWRITTEN) {
651 renderer->row[offset] = color | (current & FLAG_OBJWIN);
652 } else if (!(color & FLAG_TARGET_1) || !(current & FLAG_TARGET_2)) {
653 renderer->row[offset] = color | FLAG_FINALIZED;
654 } else {
655 renderer->row[offset] = _mix(renderer->bldb, current, renderer->blda, color) | FLAG_FINALIZED;
656 }
657 } else {
658 if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
659 renderer->row[offset] = _mix(renderer->blda, current, renderer->bldb, color) | FLAG_FINALIZED;
660 } else {
661 renderer->row[offset] = current | FLAG_FINALIZED;
662 }
663 }
664}
665
666#define BACKGROUND_DRAW_PIXEL_16_NORMAL \
667 pixelData = tileData & 0xF; \
668 current = renderer->row[outX]; \
669 if (pixelData && !(current & FLAG_FINALIZED)) { \
670 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) { \
671 _composite(renderer, outX, renderer->normalPalette[pixelData | paletteData] | flags, current); \
672 } \
673 } \
674 tileData >>= 4;
675
676#define BACKGROUND_DRAW_PIXEL_16_VARIANT \
677 pixelData = tileData & 0xF; \
678 current = renderer->row[outX]; \
679 if (tileData & 0xF && !(current & FLAG_FINALIZED)) { \
680 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) { \
681 _composite(renderer, outX, renderer->variantPalette[pixelData | paletteData] | flags, current); \
682 } \
683 } \
684 tileData >>= 4;
685
686#define BACKGROUND_DRAW_PIXEL_256_NORMAL \
687 pixelData = tileData & 0xFF; \
688 current = renderer->row[outX]; \
689 if (pixelData && !(current & FLAG_FINALIZED)) { \
690 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) { \
691 _composite(renderer, outX, renderer->normalPalette[pixelData] | flags, current); \
692 } \
693 } \
694 tileData >>= 8;
695
696#define BACKGROUND_DRAW_PIXEL_256_VARIANT \
697 pixelData = tileData & 0xFF; \
698 current = renderer->row[outX]; \
699 if (pixelData && !(renderer->row[outX] & FLAG_FINALIZED)) { \
700 if (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly) { \
701 _composite(renderer, outX, renderer->variantPalette[pixelData] | flags, current); \
702 } \
703 } \
704 tileData >>= 8;
705
706#define BACKGROUND_TEXT_SELECT_CHARACTER \
707 localX = tileX * 8 + inX; \
708 xBase = localX & 0xF8; \
709 if (background->size & 1) { \
710 xBase += (localX & 0x100) << 5; \
711 } \
712 screenBase = (background->screenBase >> 1) + (xBase >> 3) + (yBase << 2); \
713 mapData.packed = renderer->d.vram[screenBase]; \
714 if (!mapData.vflip) { \
715 localY = inY & 0x7; \
716 } else { \
717 localY = 7 - (inY & 0x7); \
718 }
719
720#define BACKGROUND_MODE_0_TILE_16_LOOP(TYPE) \
721 uint32_t tileData; \
722 uint32_t current; \
723 int paletteData, pixelData; \
724 for (; tileX < tileEnd; ++tileX) { \
725 BACKGROUND_TEXT_SELECT_CHARACTER; \
726 paletteData = mapData.palette << 4; \
727 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY; \
728 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
729 if (tileData) { \
730 if (!mapData.hflip) { \
731 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
732 ++outX; \
733 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
734 ++outX; \
735 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
736 ++outX; \
737 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
738 ++outX; \
739 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
740 ++outX; \
741 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
742 ++outX; \
743 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
744 ++outX; \
745 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
746 ++outX; \
747 } else { \
748 outX += 7; \
749 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
750 --outX; \
751 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
752 --outX; \
753 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
754 --outX; \
755 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
756 --outX; \
757 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
758 --outX; \
759 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
760 --outX; \
761 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
762 --outX; \
763 BACKGROUND_DRAW_PIXEL_16_ ## TYPE; \
764 outX += 8; \
765 } \
766 } else { \
767 outX += 8; \
768 } \
769 }
770
771#define BACKGROUND_MODE_0_TILE_256_LOOP(TYPE) \
772 uint32_t tileData; \
773 uint32_t current; \
774 int pixelData; \
775 for (; tileX < tileEnd; ++tileX) { \
776 BACKGROUND_TEXT_SELECT_CHARACTER; \
777 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1); \
778 if (!mapData.hflip) { \
779 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
780 if (tileData) { \
781 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
782 ++outX; \
783 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
784 ++outX; \
785 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
786 ++outX; \
787 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
788 ++outX; \
789 } else { \
790 outX += 4; \
791 } \
792 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
793 if (tileData) { \
794 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
795 ++outX; \
796 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
797 ++outX; \
798 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
799 ++outX; \
800 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
801 ++outX; \
802 } else { \
803 outX += 4; \
804 } \
805 } else { \
806 uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \
807 if (tileData) { \
808 outX += 3; \
809 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
810 --outX; \
811 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
812 --outX; \
813 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
814 --outX; \
815 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
816 outX += 4; \
817 } else { \
818 outX += 4; \
819 } \
820 tileData = ((uint32_t*)renderer->d.vram)[charBase]; \
821 if (tileData) { \
822 outX += 3; \
823 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
824 --outX; \
825 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
826 --outX; \
827 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
828 --outX; \
829 BACKGROUND_DRAW_PIXEL_256_ ## TYPE; \
830 outX += 4; \
831 } else { \
832 outX += 4; \
833 } \
834 } \
835 }
836
837#define PREPARE_OBJWIN \
838 int objwinSlowPath = renderer->dispcnt.objwinEnable; \
839 int objwinOnly = 0; \
840 if (objwinSlowPath) { \
841 switch (background->index) { \
842 case 0: \
843 objwinSlowPath = renderer->objwin.bg0Enable != renderer->currentWindow.bg0Enable; \
844 objwinOnly = renderer->objwin.bg0Enable; \
845 break; \
846 case 1: \
847 objwinSlowPath = renderer->objwin.bg1Enable != renderer->currentWindow.bg1Enable; \
848 objwinOnly = renderer->objwin.bg1Enable; \
849 break; \
850 case 2: \
851 objwinSlowPath = renderer->objwin.bg2Enable != renderer->currentWindow.bg2Enable; \
852 objwinOnly = renderer->objwin.bg2Enable; \
853 break; \
854 case 3: \
855 objwinSlowPath = renderer->objwin.bg3Enable != renderer->currentWindow.bg3Enable; \
856 objwinOnly = renderer->objwin.bg3Enable; \
857 break; \
858 } \
859 }
860
861static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) {
862 int inX = renderer->start + background->x;
863 int inY = y + background->y;
864 union GBATextMapData mapData;
865 PREPARE_OBJWIN;
866
867 unsigned yBase = inY & 0xF8;
868 if (background->size == 2) {
869 yBase += inY & 0x100;
870 } else if (background->size == 3) {
871 yBase += (inY & 0x100) << 1;
872 }
873
874 int localX;
875 int localY;
876
877 unsigned xBase;
878
879 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
880 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA);
881 flags |= FLAG_TARGET_2 * background->target2;
882
883 uint32_t screenBase;
884 uint32_t charBase;
885 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
886
887 int outX = renderer->start;
888 int tileX = 0;
889 int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3;
890 if (inX & 0x7) {
891 uint32_t tileData;
892 uint32_t current;
893 int pixelData, paletteData;
894 int mod8 = inX & 0x7;
895 BACKGROUND_TEXT_SELECT_CHARACTER;
896
897 int end = outX + 0x8 - mod8;
898 if (!background->multipalette) {
899 paletteData = mapData.palette << 4;
900 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
901 tileData = ((uint32_t*)renderer->d.vram)[charBase];
902 if (!mapData.hflip) {
903 tileData >>= 4 * mod8;
904 if (!variant) {
905 for (; outX < end; ++outX) {
906 BACKGROUND_DRAW_PIXEL_16_NORMAL;
907 }
908 } else {
909 for (; outX < end; ++outX) {
910 BACKGROUND_DRAW_PIXEL_16_VARIANT;
911 }
912 }
913 } else {
914 if (!variant) {
915 for (outX = end - 1; outX >= renderer->start; --outX) {
916 BACKGROUND_DRAW_PIXEL_16_NORMAL;
917 }
918 } else {
919 for (outX = end - 1; outX >= renderer->start; --outX) {
920 BACKGROUND_DRAW_PIXEL_16_VARIANT;
921 }
922 }
923 }
924 } else {
925 // TODO: hflip
926 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
927 int end2 = end - 4;
928 int shift = inX & 0x3;
929 if (end2 > 0) {
930 tileData = ((uint32_t*)renderer->d.vram)[charBase];
931 tileData >>= 8 * shift;
932 shift = 0;
933 if (!variant) {
934 for (; outX < end2; ++outX) {
935 BACKGROUND_DRAW_PIXEL_256_NORMAL;
936 }
937 } else {
938 for (; outX < end2; ++outX) {
939 BACKGROUND_DRAW_PIXEL_256_VARIANT;
940 }
941 }
942 }
943
944 tileData = ((uint32_t*)renderer->d.vram)[charBase + 1];
945 tileData >>= 8 * shift;
946 if (!variant) {
947 for (; outX < end; ++outX) {
948 BACKGROUND_DRAW_PIXEL_256_NORMAL;
949 }
950 } else {
951 for (; outX < end; ++outX) {
952 BACKGROUND_DRAW_PIXEL_256_VARIANT;
953 }
954 }
955 }
956 }
957 if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) {
958 tileX = tileEnd;
959 uint32_t tileData;
960 uint32_t current;
961 int pixelData, paletteData;
962 int mod8 = (inX + renderer->end - renderer->start) & 0x7;
963 BACKGROUND_TEXT_SELECT_CHARACTER;
964
965 int end = 0x8 - mod8;
966 if (!background->multipalette) {
967 charBase = ((background->charBase + (mapData.tile << 5)) >> 2) + localY;
968 tileData = ((uint32_t*)renderer->d.vram)[charBase];
969 paletteData = mapData.palette << 4;
970 if (!mapData.hflip) {
971 outX = renderer->end - mod8;
972 if (outX < 0) {
973 tileData >>= 4 * -outX;
974 outX = 0;
975 }
976 if (!variant) {
977 for (; outX < renderer->end; ++outX) {
978 BACKGROUND_DRAW_PIXEL_16_NORMAL;
979 }
980 } else {
981 for (; outX < renderer->end; ++outX) {
982 BACKGROUND_DRAW_PIXEL_16_VARIANT;
983 }
984 }
985 } else {
986 tileData >>= 4 * (0x8 - mod8);
987 int end2 = renderer->end - 8;
988 if (end2 < -1) {
989 end2 = -1;
990 }
991 if (!variant) {
992 for (outX = renderer->end - 1; outX > end2; --outX) {
993 BACKGROUND_DRAW_PIXEL_16_NORMAL;
994 }
995 } else {
996 for (outX = renderer->end - 1; outX > end2; --outX) {
997 BACKGROUND_DRAW_PIXEL_16_VARIANT;
998 }
999 }
1000 }
1001 } else {
1002 // TODO: hflip
1003 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
1004 outX = renderer->end - 8 + end;
1005 int end2 = 4 - end;
1006 if (end2 > 0) {
1007 tileData = ((uint32_t*)renderer->d.vram)[charBase];
1008 if (!variant) {
1009 for (; outX < renderer->end - end2; ++outX) {
1010 BACKGROUND_DRAW_PIXEL_256_NORMAL;
1011 }
1012 } else {
1013 for (; outX < renderer->end - end2; ++outX) {
1014 BACKGROUND_DRAW_PIXEL_256_VARIANT;
1015 }
1016 }
1017 ++charBase;
1018 }
1019
1020 tileData = ((uint32_t*)renderer->d.vram)[charBase];
1021 if (!variant) {
1022 for (; outX < renderer->end; ++outX) {
1023 BACKGROUND_DRAW_PIXEL_256_NORMAL;
1024 }
1025 } else {
1026 for (; outX < renderer->end; ++outX) {
1027 BACKGROUND_DRAW_PIXEL_256_VARIANT;
1028 }
1029 }
1030 }
1031
1032 tileX = (inX & 0x7) != 0;
1033 outX = renderer->start + tileX * 8 - (inX & 0x7);
1034 }
1035
1036 if (!background->multipalette) {
1037 if (!variant) {
1038 BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
1039 } else {
1040 BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
1041 }
1042 } else {
1043 if (!variant) {
1044 BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
1045 } else {
1046 BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
1047 }
1048 }
1049}
1050
1051#define BACKGROUND_BITMAP_INIT \
1052 (void)(unused); \
1053 int32_t x = background->sx - background->dx; \
1054 int32_t y = background->sy - background->dy; \
1055 int32_t localX; \
1056 int32_t localY; \
1057 \
1058 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
1059 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
1060 flags |= FLAG_TARGET_2 * background->target2; \
1061 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1062
1063#define BACKGROUND_BITMAP_ITERATE(W, H) \
1064 x += background->dx; \
1065 y += background->dy; \
1066 \
1067 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
1068 continue; \
1069 } else { \
1070 localX = x; \
1071 localY = y; \
1072 }
1073
1074static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1075 int sizeAdjusted = 0x8000 << background->size;
1076
1077 BACKGROUND_BITMAP_INIT;
1078 PREPARE_OBJWIN;
1079
1080 uint32_t screenBase = background->screenBase;
1081 uint32_t charBase = background->charBase;
1082 uint8_t mapData;
1083 uint8_t tileData;
1084
1085 int outX;
1086 for (outX = renderer->start; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1087 x += background->dx;
1088 y += background->dy;
1089
1090 if (background->overflow) {
1091 localX = x & (sizeAdjusted - 1);
1092 localY = y & (sizeAdjusted - 1);
1093 } else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
1094 continue;
1095 } else {
1096 localX = x;
1097 localY = y;
1098 }
1099 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1100 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1101
1102 uint32_t current = renderer->row[outX];
1103 if (tileData && !(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1104 if (!variant) {
1105 _composite(renderer, outX, renderer->normalPalette[tileData] | flags, current);
1106 } else {
1107 _composite(renderer, outX, renderer->variantPalette[tileData] | flags, current);
1108 }
1109 }
1110 }
1111}
1112
1113static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1114 BACKGROUND_BITMAP_INIT;
1115 PREPARE_OBJWIN;
1116
1117 uint16_t color;
1118 uint32_t color32;
1119
1120 int outX;
1121 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1122 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1123
1124 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1125 color32 = 0;
1126 color32 |= (color << 3) & 0xF8;
1127 color32 |= (color << 6) & 0xF800;
1128 color32 |= (color << 9) & 0xF80000;
1129
1130 uint32_t current = renderer->row[outX];
1131 if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1132 if (!variant) {
1133 _composite(renderer, outX, color32 | flags, current);
1134 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1135 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags, current);
1136 } else if (renderer->blendEffect == BLEND_DARKEN) {
1137 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags, current);
1138 }
1139 }
1140 }
1141}
1142
1143static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1144 BACKGROUND_BITMAP_INIT;
1145 PREPARE_OBJWIN;
1146
1147 uint16_t color;
1148 uint32_t offset = 0;
1149 if (renderer->dispcnt.frameSelect) {
1150 offset = 0xA000;
1151 }
1152
1153 int outX;
1154 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1155 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1156
1157 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1158
1159 uint32_t current = renderer->row[outX];
1160 if (color && !(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1161 if (!variant) {
1162 _composite(renderer, outX, renderer->normalPalette[color] | flags, current);
1163 } else {
1164 _composite(renderer, outX, renderer->variantPalette[color] | flags, current);
1165 }
1166 }
1167 }
1168}
1169
1170static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1171 BACKGROUND_BITMAP_INIT;
1172 PREPARE_OBJWIN;
1173
1174 uint32_t color;
1175 uint32_t offset = 0;
1176 if (renderer->dispcnt.frameSelect) {
1177 offset = 0xA000;
1178 }
1179
1180 int outX;
1181 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1182 BACKGROUND_BITMAP_ITERATE(160, 128);
1183
1184 color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1185#ifndef COLOR_16_BIT
1186 color_t color32 = 0;
1187 color32 |= (color << 9) & 0xF80000;
1188 color32 |= (color << 3) & 0xF8;
1189 color32 |= (color << 6) & 0xF800;
1190 color = color32;
1191#endif
1192
1193 uint32_t current = renderer->row[outX];
1194 if (!(current & FLAG_FINALIZED) && (!objwinSlowPath || !(current & FLAG_OBJWIN) != objwinOnly)) {
1195 if (!variant) {
1196 _composite(renderer, outX, color | flags, current);
1197 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1198 _composite(renderer, outX, _brighten(color, renderer->bldy) | flags, current);
1199 } else if (renderer->blendEffect == BLEND_DARKEN) {
1200 _composite(renderer, outX, _darken(color, renderer->bldy) | flags, current);
1201 }
1202 }
1203 }
1204}
1205
1206static const int _objSizes[32] = {
1207 8, 8,
1208 16, 16,
1209 32, 32,
1210 64, 64,
1211 16, 8,
1212 32, 8,
1213 32, 16,
1214 64, 32,
1215 8, 16,
1216 8, 32,
1217 16, 32,
1218 32, 64,
1219 0, 0,
1220 0, 0,
1221 0, 0,
1222 0, 0
1223};
1224
1225#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1226 SPRITE_YBASE_ ## DEPTH(inY); \
1227 int outX = x >= start ? x : start; \
1228 int condition = x + width; \
1229 if (end < condition) { \
1230 condition = end; \
1231 } \
1232 for (; outX < condition; ++outX) { \
1233 int inX = outX - x; \
1234 if (sprite->hflip) { \
1235 inX = width - inX - 1; \
1236 } \
1237 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1238 continue; \
1239 } \
1240 SPRITE_XBASE_ ## DEPTH(inX); \
1241 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1242 }
1243
1244#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1245 int outX; \
1246 for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1247 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1248 continue; \
1249 } \
1250 int inX = outX - x; \
1251 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1252 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1253 \
1254 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1255 continue; \
1256 } \
1257 \
1258 SPRITE_YBASE_ ## DEPTH(localY); \
1259 SPRITE_XBASE_ ## DEPTH(localX); \
1260 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1261 }
1262
1263#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1264#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1265
1266#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1267 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1268 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1269 if (tileData && !(renderer->spriteLayer[outX])) { \
1270 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1271 }
1272
1273#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1274 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1275 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1276 if (tileData && !(renderer->spriteLayer[outX])) { \
1277 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1278 }
1279
1280#define SPRITE_DRAW_PIXEL_16_OBJWIN(localX) \
1281 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1282 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1283 if (tileData) { \
1284 renderer->row[outX] |= FLAG_OBJWIN; \
1285 }
1286
1287#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1288#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1289
1290#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1291 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1292 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1293 if (tileData && !(renderer->spriteLayer[outX])) { \
1294 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1295 }
1296
1297#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1298 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1299 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1300 if (tileData && !(renderer->spriteLayer[outX])) { \
1301 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1302 }
1303
1304#define SPRITE_DRAW_PIXEL_256_OBJWIN(localX) \
1305 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1306 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1307 if (tileData) { \
1308 renderer->row[outX] |= FLAG_OBJWIN; \
1309 }
1310
1311static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1312 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1313 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1314 int start = renderer->start;
1315 int end = renderer->end;
1316 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1317 return;
1318 }
1319 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1320 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1321 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1322 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1323 int x = sprite->x;
1324 int inY = y - sprite->y;
1325 if (sprite->y + height - 256 >= 0) {
1326 inY += 256;
1327 }
1328 if (sprite->vflip) {
1329 inY = height - inY - 1;
1330 }
1331 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1332 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1333 if (!sprite->multipalette) {
1334 if (flags & FLAG_OBJWIN) {
1335 SPRITE_NORMAL_LOOP(16, OBJWIN);
1336 } else if (!variant) {
1337 SPRITE_NORMAL_LOOP(16, NORMAL);
1338 } else {
1339 SPRITE_NORMAL_LOOP(16, VARIANT);
1340 }
1341 } else {
1342 if (flags & FLAG_OBJWIN) {
1343 SPRITE_NORMAL_LOOP(256, OBJWIN);
1344 } else if (!variant) {
1345 SPRITE_NORMAL_LOOP(256, NORMAL);
1346 } else {
1347 SPRITE_NORMAL_LOOP(256, VARIANT);
1348 }
1349 }
1350}
1351
1352static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1353 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1354 int totalWidth = width << sprite->doublesize;
1355 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1356 int totalHeight = height << sprite->doublesize;
1357 int start = renderer->start;
1358 int end = renderer->end;
1359 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1360 return;
1361 }
1362 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1363 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1364 flags |= FLAG_TARGET_2 * renderer->target2Obj;
1365 flags |= FLAG_OBJWIN * (sprite->mode == OBJ_MODE_OBJWIN);
1366 int x = sprite->x;
1367 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1368 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1369 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1370 int inY = y - sprite->y;
1371 if (inY < 0) {
1372 inY += 256;
1373 }
1374 if (!sprite->multipalette) {
1375 if (flags & FLAG_OBJWIN) {
1376 SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
1377 } else if (!variant) {
1378 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1379 } else {
1380 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1381 }
1382 } else {
1383 if (flags & FLAG_OBJWIN) {
1384 SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
1385 } else if (!variant) {
1386 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1387 } else {
1388 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1389 }
1390 }
1391}
1392
1393static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, unsigned priority) {
1394 int x;
1395 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
1396 uint32_t color = renderer->spriteLayer[x];
1397 uint32_t current = renderer->row[x];
1398 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(current & FLAG_FINALIZED)) {
1399 _composite(renderer, x, color & ~FLAG_FINALIZED, current);
1400 }
1401 }
1402}
1403
1404static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1405 int i;
1406 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1407 for (i = 0; i < 512; ++i) {
1408 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1409 }
1410 } else if (renderer->blendEffect == BLEND_DARKEN) {
1411 for (i = 0; i < 512; ++i) {
1412 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1413 }
1414 } else {
1415 for (i = 0; i < 512; ++i) {
1416 renderer->variantPalette[i] = renderer->normalPalette[i];
1417 }
1418 }
1419}
1420
1421static inline color_t _brighten(color_t color, int y) {
1422 color_t c = 0;
1423 color_t a;
1424#ifdef COLOR_16_BIT
1425 a = color & 0x1F;
1426 c |= (a + ((0x1F - a) * y) / 16) & 0x1F;
1427
1428 a = color & 0x3E0;
1429 c |= (a + ((0x3E0 - a) * y) / 16) & 0x3E0;
1430
1431 a = color & 0x7C00;
1432 c |= (a + ((0x7C00 - a) * y) / 16) & 0x7C00;
1433#else
1434 a = color & 0xF8;
1435 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1436
1437 a = color & 0xF800;
1438 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1439
1440 a = color & 0xF80000;
1441 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1442#endif
1443 return c;
1444}
1445
1446static inline color_t _darken(color_t color, int y) {
1447 color_t c = 0;
1448 color_t a;
1449#ifdef COLOR_16_BIT
1450 a = color & 0x1F;
1451 c |= (a - (a * y) / 16) & 0x1F;
1452
1453 a = color & 0x3E0;
1454 c |= (a - (a * y) / 16) & 0x3E0;
1455
1456 a = color & 0x7C00;
1457 c |= (a - (a * y) / 16) & 0x7C00;
1458#else
1459 a = color & 0xF8;
1460 c |= (a - (a * y) / 16) & 0xF8;
1461
1462 a = color & 0xF800;
1463 c |= (a - (a * y) / 16) & 0xF800;
1464
1465 a = color & 0xF80000;
1466 c |= (a - (a * y) / 16) & 0xF80000;
1467#endif
1468 return c;
1469}
1470
1471static color_t _mix(int weightA, color_t colorA, int weightB, color_t colorB) {
1472 color_t c = 0;
1473 color_t a, b;
1474#ifdef COLOR_16_BIT
1475 a = colorA & 0x1F;
1476 b = colorB & 0x1F;
1477 c |= ((a * weightA + b * weightB) / 16) & 0x3F;
1478 if (c & 0x0020) {
1479 c = 0x001F;
1480 }
1481
1482 a = colorA & 0x3E0;
1483 b = colorB & 0x3E0;
1484 c |= ((a * weightA + b * weightB) / 16) & 0x7E0;
1485 if (c & 0x0400) {
1486 c |= 0x03E0;
1487 }
1488
1489 a = colorA & 0x7C00;
1490 b = colorB & 0x7C00;
1491 c |= ((a * weightA + b * weightB) / 16) & 0xFC00;
1492 if (c & 0x8000) {
1493 c |= 0x7C00;
1494 }
1495#else
1496 a = colorA & 0xF8;
1497 b = colorB & 0xF8;
1498 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1499 if (c & 0x00000100) {
1500 c = 0x000000F8;
1501 }
1502
1503 a = colorA & 0xF800;
1504 b = colorB & 0xF800;
1505 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1506 if (c & 0x00010000) {
1507 c = (c & 0x000000F8) | 0x0000F800;
1508 }
1509
1510 a = colorA & 0xF80000;
1511 b = colorB & 0xF80000;
1512 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1513 if (c & 0x01000000) {
1514 c = (c & 0x0000F8F8) | 0x00F80000;
1515 }
1516#endif
1517 return c;
1518}