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 outX = renderer->end - mod8;
900 if (outX < 0) {
901 tileData >>= 4 * -outX;
902 outX = 0;
903 }
904 if (!variant) {
905 for (; outX < renderer->end; ++outX) {
906 BACKGROUND_DRAW_PIXEL_16_NORMAL;
907 }
908 } else {
909 for (; outX < renderer->end; ++outX) {
910 BACKGROUND_DRAW_PIXEL_16_VARIANT;
911 }
912 }
913 } else {
914 tileData >>= 4 * (0x8 - mod8);
915 int end2 = renderer->end - 8;
916 if (end2 < -1) {
917 end2 = -1;
918 }
919 if (!variant) {
920 for (outX = renderer->end - 1; outX > end2; --outX) {
921 BACKGROUND_DRAW_PIXEL_16_NORMAL;
922 }
923 } else {
924 for (outX = renderer->end - 1; outX > end2; --outX) {
925 BACKGROUND_DRAW_PIXEL_16_VARIANT;
926 }
927 }
928 }
929 } else {
930 // TODO: hflip
931 charBase = ((background->charBase + (mapData.tile << 6)) >> 2) + (localY << 1);
932 outX = renderer->end - 8 + end;
933 int end2 = 4 - end;
934 if (end2 > 0) {
935 tileData = ((uint32_t*)renderer->d.vram)[charBase];
936 if (!variant) {
937 for (; outX < renderer->end - end2; ++outX) {
938 BACKGROUND_DRAW_PIXEL_256_NORMAL;
939 }
940 } else {
941 for (; outX < renderer->end - end2; ++outX) {
942 BACKGROUND_DRAW_PIXEL_256_VARIANT;
943 }
944 }
945 ++charBase;
946 }
947
948 tileData = ((uint32_t*)renderer->d.vram)[charBase];
949 if (!variant) {
950 for (; outX < renderer->end; ++outX) {
951 BACKGROUND_DRAW_PIXEL_256_NORMAL;
952 }
953 } else {
954 for (; outX < renderer->end; ++outX) {
955 BACKGROUND_DRAW_PIXEL_256_VARIANT;
956 }
957 }
958 }
959
960 tileX = (inX & 0x7) != 0;
961 outX = renderer->start + tileX * 8 - (inX & 0x7);
962 }
963
964 if (!background->multipalette) {
965 if (!variant) {
966 BACKGROUND_MODE_0_TILE_16_LOOP(NORMAL);
967 } else {
968 BACKGROUND_MODE_0_TILE_16_LOOP(VARIANT);
969 }
970 } else {
971 if (!variant) {
972 BACKGROUND_MODE_0_TILE_256_LOOP(NORMAL);
973 } else {
974 BACKGROUND_MODE_0_TILE_256_LOOP(VARIANT);
975 }
976 }
977}
978
979#define BACKGROUND_BITMAP_INIT \
980 (void)(unused); \
981 int32_t x = background->sx - background->dx; \
982 int32_t y = background->sy - background->dy; \
983 int32_t localX; \
984 int32_t localY; \
985 \
986 int flags = (background->priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND; \
987 flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA); \
988 flags |= FLAG_TARGET_2 * background->target2; \
989 int variant = background->target1 && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
990
991#define BACKGROUND_BITMAP_ITERATE(W, H) \
992 x += background->dx; \
993 y += background->dy; \
994 \
995 if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
996 continue; \
997 } else { \
998 localX = x; \
999 localY = y; \
1000 }
1001
1002static void _drawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1003 int sizeAdjusted = 0x8000 << background->size;
1004
1005 BACKGROUND_BITMAP_INIT;
1006
1007 uint32_t screenBase = background->screenBase;
1008 uint32_t charBase = background->charBase;
1009 uint8_t mapData;
1010 uint8_t tileData;
1011
1012 int outX;
1013 for (outX = renderer->start; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1014 x += background->dx;
1015 y += background->dy;
1016
1017 if (background->overflow) {
1018 localX = x & (sizeAdjusted - 1);
1019 localY = y & (sizeAdjusted - 1);
1020 } else if (x < 0 || y < 0 || x >= sizeAdjusted || y >= sizeAdjusted) {
1021 continue;
1022 } else {
1023 localX = x;
1024 localY = y;
1025 }
1026 mapData = ((uint8_t*)renderer->d.vram)[screenBase + (localX >> 11) + (((localY >> 7) & 0x7F0) << background->size)];
1027 tileData = ((uint8_t*)renderer->d.vram)[charBase + (mapData << 6) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8)];
1028
1029 if (tileData && !(renderer->row[outX] & FLAG_FINALIZED)) {
1030 if (!variant) {
1031 _composite(renderer, outX, renderer->normalPalette[tileData] | flags);
1032 } else {
1033 _composite(renderer, outX, renderer->variantPalette[tileData] | flags);
1034 }
1035 }
1036 }
1037}
1038
1039static void _drawBackgroundMode3(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1040 BACKGROUND_BITMAP_INIT;
1041
1042 uint16_t color;
1043 uint32_t color32;
1044
1045 int outX;
1046 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1047 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1048
1049 color = ((uint16_t*)renderer->d.vram)[(localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1050 color32 = 0;
1051 color32 |= (color << 3) & 0xF8;
1052 color32 |= (color << 6) & 0xF800;
1053 color32 |= (color << 9) & 0xF80000;
1054
1055 if (!(renderer->row[outX] & FLAG_FINALIZED)) {
1056 if (!variant) {
1057 _composite(renderer, outX, color32 | flags);
1058 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1059 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
1060 } else if (renderer->blendEffect == BLEND_DARKEN) {
1061 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
1062 }
1063 }
1064 }
1065}
1066
1067static void _drawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1068 BACKGROUND_BITMAP_INIT;
1069
1070 uint16_t color;
1071 uint32_t offset = 0;
1072 if (renderer->dispcnt.frameSelect) {
1073 offset = 0xA000;
1074 }
1075
1076 int outX;
1077 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1078 BACKGROUND_BITMAP_ITERATE(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
1079
1080 color = ((uint8_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * VIDEO_HORIZONTAL_PIXELS];
1081
1082 if (color && !(renderer->row[outX] & FLAG_FINALIZED)) {
1083 if (!variant) {
1084 _composite(renderer, outX, renderer->normalPalette[color] | flags);
1085 } else {
1086 _composite(renderer, outX, renderer->variantPalette[color] | flags);
1087 }
1088 }
1089 }
1090}
1091
1092static void _drawBackgroundMode5(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int unused) {
1093 BACKGROUND_BITMAP_INIT;
1094
1095 uint16_t color;
1096 uint32_t color32;
1097 uint32_t offset = 0;
1098 if (renderer->dispcnt.frameSelect) {
1099 offset = 0xA000;
1100 }
1101
1102 int outX;
1103 for (outX = 0; outX < VIDEO_HORIZONTAL_PIXELS; ++outX) {
1104 BACKGROUND_BITMAP_ITERATE(160, 128);
1105
1106 color = ((uint16_t*)renderer->d.vram)[offset + (localX >> 8) + (localY >> 8) * 160];
1107 color32 = 0;
1108 color32 |= (color << 3) & 0xF8;
1109 color32 |= (color << 6) & 0xF800;
1110 color32 |= (color << 9) & 0xF80000;
1111
1112 if (!(renderer->row[outX] & FLAG_FINALIZED)) {
1113 if (!variant) {
1114 _composite(renderer, outX, color32 | flags);
1115 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
1116 _composite(renderer, outX, _brighten(color32, renderer->bldy) | flags);
1117 } else if (renderer->blendEffect == BLEND_DARKEN) {
1118 _composite(renderer, outX, _darken(color32, renderer->bldy) | flags);
1119 }
1120 }
1121 }
1122}
1123
1124static const int _objSizes[32] = {
1125 8, 8,
1126 16, 16,
1127 32, 32,
1128 64, 64,
1129 16, 8,
1130 32, 8,
1131 32, 16,
1132 64, 32,
1133 8, 16,
1134 8, 32,
1135 16, 32,
1136 32, 64,
1137 0, 0,
1138 0, 0,
1139 0, 0,
1140 0, 0
1141};
1142
1143#define SPRITE_NORMAL_LOOP(DEPTH, TYPE) \
1144 SPRITE_YBASE_ ## DEPTH(inY); \
1145 int outX = x >= start ? x : start; \
1146 int condition = x + width; \
1147 if (end < condition) { \
1148 condition = end; \
1149 } \
1150 for (; outX < condition; ++outX) { \
1151 int inX = outX - x; \
1152 if (sprite->hflip) { \
1153 inX = width - inX - 1; \
1154 } \
1155 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1156 continue; \
1157 } \
1158 SPRITE_XBASE_ ## DEPTH(inX); \
1159 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(inX); \
1160 }
1161
1162#define SPRITE_TRANSFORMED_LOOP(DEPTH, TYPE) \
1163 int outX; \
1164 for (outX = x >= start ? x : start; outX < x + totalWidth && outX < end; ++outX) { \
1165 if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
1166 continue; \
1167 } \
1168 int inX = outX - x; \
1169 int localX = ((mat->a * (inX - (totalWidth >> 1)) + mat->b * (inY - (totalHeight >> 1))) >> 8) + (width >> 1); \
1170 int localY = ((mat->c * (inX - (totalWidth >> 1)) + mat->d * (inY - (totalHeight >> 1))) >> 8) + (height >> 1); \
1171 \
1172 if (localX < 0 || localX >= width || localY < 0 || localY >= height) { \
1173 continue; \
1174 } \
1175 \
1176 SPRITE_YBASE_ ## DEPTH(localY); \
1177 SPRITE_XBASE_ ## DEPTH(localX); \
1178 SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
1179 }
1180
1181#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
1182#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width >> 1 : 0x80) + (localY & 0x7) * 4;
1183
1184#define SPRITE_DRAW_PIXEL_16_NORMAL(localX) \
1185 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1186 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1187 if (tileData && !(renderer->spriteLayer[outX])) { \
1188 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1189 }
1190
1191#define SPRITE_DRAW_PIXEL_16_VARIANT(localX) \
1192 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1193 tileData = (tileData >> ((localX & 3) << 2)) & 0xF; \
1194 if (tileData && !(renderer->spriteLayer[outX])) { \
1195 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData | (sprite->palette << 4)] | flags; \
1196 }
1197
1198#define SPRITE_XBASE_256(localX) unsigned xBase = (localX & ~0x7) * 8 + (localX & 6);
1199#define SPRITE_YBASE_256(localY) unsigned yBase = (localY & ~0x7) * (renderer->dispcnt.objCharacterMapping ? width : 0x80) + (localY & 0x7) * 8;
1200
1201#define SPRITE_DRAW_PIXEL_256_NORMAL(localX) \
1202 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1203 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1204 if (tileData && !(renderer->spriteLayer[outX])) { \
1205 renderer->spriteLayer[outX] = renderer->normalPalette[0x100 | tileData] | flags; \
1206 }
1207
1208#define SPRITE_DRAW_PIXEL_256_VARIANT(localX) \
1209 uint16_t tileData = renderer->d.vram[(yBase + charBase + xBase) >> 1]; \
1210 tileData = (tileData >> ((localX & 1) << 3)) & 0xFF; \
1211 if (tileData && !(renderer->spriteLayer[outX])) { \
1212 renderer->spriteLayer[outX] = renderer->variantPalette[0x100 | tileData] | flags; \
1213 }
1214
1215static void _preprocessSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBAObj* sprite, int y) {
1216 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1217 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1218 int start = renderer->start;
1219 int end = renderer->end;
1220 if ((y < sprite->y && (sprite->y + height - 256 < 0 || y >= sprite->y + height - 256)) || y >= sprite->y + height) {
1221 return;
1222 }
1223 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1224 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1225 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1226 int x = sprite->x;
1227 int inY = y - sprite->y;
1228 if (sprite->y + height - 256 >= 0) {
1229 inY += 256;
1230 }
1231 if (sprite->vflip) {
1232 inY = height - inY - 1;
1233 }
1234 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1235 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1236 if (!sprite->multipalette) {
1237 if (!variant) {
1238 SPRITE_NORMAL_LOOP(16, NORMAL);
1239 } else {
1240 SPRITE_NORMAL_LOOP(16, VARIANT);
1241 }
1242 } else {
1243 if (!variant) {
1244 SPRITE_NORMAL_LOOP(256, NORMAL);
1245 } else {
1246 SPRITE_NORMAL_LOOP(256, VARIANT);
1247 }
1248 }
1249}
1250
1251static void _preprocessTransformedSprite(struct GBAVideoSoftwareRenderer* renderer, struct GBATransformedObj* sprite, int y) {
1252 int width = _objSizes[sprite->shape * 8 + sprite->size * 2];
1253 int totalWidth = width << sprite->doublesize;
1254 int height = _objSizes[sprite->shape * 8 + sprite->size * 2 + 1];
1255 int totalHeight = height << sprite->doublesize;
1256 int start = renderer->start;
1257 int end = renderer->end;
1258 if ((y < sprite->y && (sprite->y + totalHeight - 256 < 0 || y >= sprite->y + totalHeight - 256)) || y >= sprite->y + totalHeight) {
1259 return;
1260 }
1261 int flags = (sprite->priority << OFFSET_PRIORITY) | FLAG_FINALIZED;
1262 flags |= FLAG_TARGET_1 * ((renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || sprite->mode == OBJ_MODE_SEMITRANSPARENT);
1263 flags |= FLAG_TARGET_2 *renderer->target2Obj;
1264 int x = sprite->x;
1265 unsigned charBase = BASE_TILE + sprite->tile * 0x20;
1266 struct GBAOAMMatrix* mat = &renderer->d.oam->mat[sprite->matIndex];
1267 int variant = renderer->target1Obj && renderer->currentWindow.blendEnable && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
1268 int inY = y - sprite->y;
1269 if (inY < 0) {
1270 inY += 256;
1271 }
1272 if (!sprite->multipalette) {
1273 if (!variant) {
1274 SPRITE_TRANSFORMED_LOOP(16, NORMAL);
1275 } else {
1276 SPRITE_TRANSFORMED_LOOP(16, VARIANT);
1277 }
1278 } else {
1279 if (!variant) {
1280 SPRITE_TRANSFORMED_LOOP(256, NORMAL);
1281 } else {
1282 SPRITE_TRANSFORMED_LOOP(256, VARIANT);
1283 }
1284 }
1285}
1286
1287static void _postprocessSprite(struct GBAVideoSoftwareRenderer* renderer, int priority) {
1288 int x;
1289 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
1290 uint32_t color = renderer->spriteLayer[x];
1291 if ((color & FLAG_FINALIZED) && (color & FLAG_PRIORITY) >> OFFSET_PRIORITY == priority && !(renderer->row[x] & FLAG_FINALIZED)) {
1292 _composite(renderer, x, color & ~FLAG_FINALIZED);
1293 }
1294 }
1295}
1296
1297static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
1298 int i;
1299 if (renderer->blendEffect == BLEND_BRIGHTEN) {
1300 for (i = 0; i < 512; ++i) {
1301 renderer->variantPalette[i] = _brighten(renderer->normalPalette[i], renderer->bldy);
1302 }
1303 } else if (renderer->blendEffect == BLEND_DARKEN) {
1304 for (i = 0; i < 512; ++i) {
1305 renderer->variantPalette[i] = _darken(renderer->normalPalette[i], renderer->bldy);
1306 }
1307 } else {
1308 for (i = 0; i < 512; ++i) {
1309 renderer->variantPalette[i] = renderer->normalPalette[i];
1310 }
1311 }
1312}
1313
1314static inline uint32_t _brighten(uint32_t color, int y) {
1315 uint32_t c = 0;
1316 uint32_t a;
1317 a = color & 0xF8;
1318 c |= (a + ((0xF8 - a) * y) / 16) & 0xF8;
1319
1320 a = color & 0xF800;
1321 c |= (a + ((0xF800 - a) * y) / 16) & 0xF800;
1322
1323 a = color & 0xF80000;
1324 c |= (a + ((0xF80000 - a) * y) / 16) & 0xF80000;
1325 return c;
1326}
1327
1328static inline uint32_t _darken(uint32_t color, int y) {
1329 uint32_t c = 0;
1330 uint32_t a;
1331 a = color & 0xF8;
1332 c |= (a - (a * y) / 16) & 0xF8;
1333
1334 a = color & 0xF800;
1335 c |= (a - (a * y) / 16) & 0xF800;
1336
1337 a = color & 0xF80000;
1338 c |= (a - (a * y) / 16) & 0xF80000;
1339 return c;
1340}
1341
1342static uint32_t _mix(int weightA, uint32_t colorA, int weightB, uint32_t colorB) {
1343 uint32_t c = 0;
1344 uint32_t a, b;
1345 a = colorA & 0xF8;
1346 b = colorB & 0xF8;
1347 c |= ((a * weightA + b * weightB) / 16) & 0x1F8;
1348 if (c & 0x00000100) {
1349 c = 0x000000F8;
1350 }
1351
1352 a = colorA & 0xF800;
1353 b = colorB & 0xF800;
1354 c |= ((a * weightA + b * weightB) / 16) & 0x1F800;
1355 if (c & 0x00010000) {
1356 c = (c & 0x000000F8) | 0x0000F800;
1357 }
1358
1359 a = colorA & 0xF80000;
1360 b = colorB & 0xF80000;
1361 c |= ((a * weightA + b * weightB) / 16) & 0x1F80000;
1362 if (c & 0x01000000) {
1363 c = (c & 0x0000F8F8) | 0x00F80000;
1364 }
1365 return c;
1366}