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