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