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