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